Aktualnie próbuję przenieść shadertoy.com shader (Atmospheric Scattering Sample, interaktywne demo z kodem) do Unity. Moduł cieniujący jest napisany w GLSL i muszę uruchomić edytor z C:\Program Files\Unity\Editor>Unity.exe -force-opengl
, aby uczynić renderowanie modułu cieniującego (w przeciwnym razie pojawia się błąd "Ten moduł cieniujący nie może być uruchamiany na tym GPU"), ale teraz nie stanowi to problemu. Problem polega na przeniesieniu tego shadera do Unity.Problemy z przenoszeniem shadertoy'a GLSL do jedności
Funkcje rozpraszania itp. Są identyczne i "możliwe do uruchomienia" w moim porcjonowanym module cieniującym, jedyne co jest potrzebne to funkcje kamery, kierunków światła i kierunku promienia. To musi być zmienione, ponieważ używa się pozycji kamery Unity, kierunku widzenia, źródeł światła i kierunków.
Główną funkcją oryginalnych wygląda następująco:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
// default ray dir
vec3 dir = ray_dir(45.0, iResolution.xy, fragCoord.xy);
// default ray origin
vec3 eye = vec3(0.0, 0.0, 2.4);
// rotate camera
mat3 rot = rot3xy(vec2(0.0, iGlobalTime * 0.5));
dir = rot * dir;
eye = rot * eye;
// sun light dir
vec3 l = vec3(0, 0, 1);
vec2 e = ray_vs_sphere(eye, dir, R);
if (e.x > e.y) {
discard;
}
vec2 f = ray_vs_sphere(eye, dir, R_INNER);
e.y = min(e.y, f.x);
vec3 I = in_scatter(eye, dir, e, l);
fragColor = vec4(I, 1.0);
}
Czytałem poprzez dokumentację tej funkcji i jak to miało pracę w https://www.shadertoy.com/howto.
shadery Obrazek realizacji funkcji mainImage() w celu wygenerowania obrazy proceduralnych poprzez obliczenie koloru dla każdego piksela. Oczekuje się, że ta funkcja będzie wywoływana raz na piksel, i jest to odpowiedzialna aplikacja hosta, która dostarczy odpowiednie dane wejściowe do i otrzyma od niej kolor wyjściowy i przypisze ją do piksela ekranu. Prototyp:
void mainImage (obecnie vec4 fragColor, w vec2 fragCoord);
gdzie fragCoord zawiera współrzędne pikseli, dla których moduł cieniujący musi obliczyć kolor. Współrzędne są w jednostkach pikselowych, w zakresie od od 0,5 do rozdzielczości-0,5, na powierzchni renderowania, gdzie rozdzielczość jest przekazywana do modułu cieniującego przez uniform iResolution (patrz poniżej).
Wynikowy kolor jest zbierany w fragColor jako czteroskładnikowy wektor , z którego ostatni jest ignorowany przez klienta. Rezultatem jest zebrana jako zmienna "out" w przewidywaniu przyszłego dodania wielu celów renderowania. w tej funkcji
Tak więc istnieją odniesienia do iGlobalTime
do sprawiają, że kamera obraca się wraz z upływem czasu i odniesienia do iResolution
na rozmiar. Wbudowałem moduł cieniujący w moduł cieniujący Unity i próbowałem naprawić i połączyć go z Unity, ale całkowicie utknąłem. Mam jakiś obraz, który wygląda „powiązane” do oryginalnego modułu cieniującego (Top jest oryginalny, Buttom aktualnego stanu jedności)
Nie jestem profesional shader, wiem tylko pewne podstawy OpenGL, ale w przeważającej części, piszę logikę gry w języku C#, więc jedyne, co mogłem zrobić, to przyjrzeć się innym przykładom cieniowania i spojrzeć na to, w jaki sposób mogę uzyskać dane o kamerze, źródłach światła itp. W tym kodzie, ale jak to tylko możliwe widzisz, nic nie działa, naprawdę.
Skopiowałem kod skeltonowy dla modułu cieniującego z https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Specular_Highlights i kilka wektorów z http://forum.unity3d.com/threads/glsl-shader.39629/.
Mam nadzieję, że ktoś może wskazać mi w jakiś sposób, jak naprawić ten moduł cieniujący/poprawnie przenieść go do jedności. Poniżej znajduje się aktualny kod shadera, wszystko co musisz zrobić, aby go odtworzyć, to utwórz nowy shader w pustym projekcie, skopiuj ten kod do środka, utwórz nowy materiał, przypisz moduł do tego materiału, dodaj kulkę i dodaj ten materiał na nim i dodaj kierunkowe światło.
Shader "Unlit/AtmoFragShader" {
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_LC("LC", Color) = (1,0,0,0) /* stuff from the testing shader, now really used */
_LP("LP", Vector) = (1,1,1,1)
}
SubShader{
Tags{ "Queue" = "Geometry" } //Is this even the right queue?
Pass{
//Tags{ "LightMode" = "ForwardBase" }
GLSLPROGRAM
/* begin port by copying in the constants */
// math const
const float PI = 3.14159265359;
const float DEG_TO_RAD = PI/180.0;
const float MAX = 10000.0;
// scatter const
const float K_R = 0.166;
const float K_M = 0.0025;
const float E = 14.3; // light intensity
const vec3 C_R = vec3(0.3, 0.7, 1.0); // 1/wavelength^4
const float G_M = -0.85; // Mie g
const float R = 1.0; /* this is the radius of the spehere? this should be set from the geometry or something.. */
const float R_INNER = 0.7;
const float SCALE_H = 4.0/(R - R_INNER);
const float SCALE_L = 1.0/(R - R_INNER);
const int NUM_OUT_SCATTER = 10;
const float FNUM_OUT_SCATTER = 10.0;
const int NUM_IN_SCATTER = 10;
const float FNUM_IN_SCATTER = 10.0;
/* begin functions. These are out of the defines because they should be accesible to anyone. */
// angle : pitch, yaw
mat3 rot3xy(vec2 angle) {
vec2 c = cos(angle);
vec2 s = sin(angle);
return mat3(
c.y, 0.0, -s.y,
s.y * s.x, c.x, c.y * s.x,
s.y * c.x, -s.x, c.y * c.x
);
}
// ray direction
vec3 ray_dir(float fov, vec2 size, vec2 pos) {
vec2 xy = pos - size * 0.5;
float cot_half_fov = tan((90.0 - fov * 0.5) * DEG_TO_RAD);
float z = size.y * 0.5 * cot_half_fov;
return normalize(vec3(xy, -z));
}
// ray intersects sphere
// e = -b +/- sqrt(b^2 - c)
vec2 ray_vs_sphere(vec3 p, vec3 dir, float r) {
float b = dot(p, dir);
float c = dot(p, p) - r * r;
float d = b * b - c;
if (d < 0.0) {
return vec2(MAX, -MAX);
}
d = sqrt(d);
return vec2(-b - d, -b + d);
}
// Mie
// g : (-0.75, -0.999)
// 3 * (1 - g^2) 1 + c^2
// F = ----------------- * -------------------------------
// 2 * (2 + g^2) (1 + g^2 - 2 * g * c)^(3/2)
float phase_mie(float g, float c, float cc) {
float gg = g * g;
float a = (1.0 - gg) * (1.0 + cc);
float b = 1.0 + gg - 2.0 * g * c;
b *= sqrt(b);
b *= 2.0 + gg;
return 1.5 * a/b;
}
// Reyleigh
// g : 0
// F = 3/4 * (1 + c^2)
float phase_reyleigh(float cc) {
return 0.75 * (1.0 + cc);
}
float density(vec3 p) {
return exp(-(length(p) - R_INNER) * SCALE_H);
}
float optic(vec3 p, vec3 q) {
vec3 step = (q - p)/FNUM_OUT_SCATTER;
vec3 v = p + step * 0.5;
float sum = 0.0;
for (int i = 0; i < NUM_OUT_SCATTER; i++) {
sum += density(v);
v += step;
}
sum *= length(step) * SCALE_L;
return sum;
}
vec3 in_scatter(vec3 o, vec3 dir, vec2 e, vec3 l) {
float len = (e.y - e.x)/FNUM_IN_SCATTER;
vec3 step = dir * len;
vec3 p = o + dir * e.x;
vec3 v = p + dir * (len * 0.5);
vec3 sum = vec3(0.0);
for (int i = 0; i < NUM_IN_SCATTER; i++) {
vec2 f = ray_vs_sphere(v, l, R);
vec3 u = v + l * f.y;
float n = (optic(p, v) + optic(v, u)) * (PI * 4.0);
sum += density(v) * exp(-n * (K_R * C_R + K_M));
v += step;
}
sum *= len * SCALE_L;
float c = dot(dir, -l);
float cc = c * c;
return sum * (K_R * C_R * phase_reyleigh(cc) + K_M * phase_mie(G_M, c, cc)) * E;
}
/* end functions */
/* vertex shader begins here*/
#ifdef VERTEX
const float SpecularContribution = 0.3;
const float DiffuseContribution = 1.0 - SpecularContribution;
uniform vec4 _LP;
varying vec2 TextureCoordinate;
varying float LightIntensity;
varying vec4 someOutput;
/* transient stuff */
varying vec3 eyeOutput;
varying vec3 dirOutput;
varying vec3 lOutput;
varying vec2 eOutput;
/* lighting stuff */
// i.e. one could #include "UnityCG.glslinc"
uniform vec3 _WorldSpaceCameraPos;
// camera position in world space
uniform mat4 _Object2World; // model matrix
uniform mat4 _World2Object; // inverse model matrix
uniform vec4 _WorldSpaceLightPos0;
// direction to or position of light source
uniform vec4 _LightColor0;
// color of light source (from "Lighting.cginc")
void main()
{
/* code from that example shader */
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);
vec3 tnorm = normalize(gl_NormalMatrix * gl_Normal);
vec3 lightVec = normalize(_LP.xyz - ecPosition);
vec3 reflectVec = reflect(-lightVec, tnorm);
vec3 viewVec = normalize(-ecPosition);
/* copied from https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Specular_Highlights for testing stuff */
//I have no idea what I'm doing, but hopefully this computes some vectors which I need
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
vec3 normalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
vec3 viewDirection = normalize(vec3(
vec4(_WorldSpaceCameraPos, 1.0)
- modelMatrix * gl_Vertex));
vec3 lightDirection;
float attenuation;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource = vec3(_WorldSpaceLightPos0
- modelMatrix * gl_Vertex);
float distance = length(vertexToLightSource);
attenuation = 1.0/distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
/* test port */
// default ray dir
//That's the direction of the camera here?
vec3 dir = viewDirection; //normalDirection;//viewDirection;// tnorm;//lightVec;//lightDirection;//normalDirection; //lightVec;//tnorm;//ray_dir(45.0, iResolution.xy, fragCoord.xy);
// default ray origin
//I think they mean the position of the camera here?
vec3 eye = vec3(_WorldSpaceCameraPos); //vec3(_WorldSpaceLightPos0); //// vec3(0.0, 0.0, 0.0); //_WorldSpaceCameraPos;//ecPosition; //vec3(0.0, 0.0, 2.4);
// rotate camera not needed, remove it
// sun light dir
//I think they mean the direciton of our directional light?
vec3 l = lightDirection;//_LightColor0.xyz; //lightDirection; //normalDirection;//normalize(vec3(_WorldSpaceLightPos0));//lightVec;// vec3(0, 0, 1);
/* this computes the intersection of the ray and the sphere.. is this really needed?*/
vec2 e = ray_vs_sphere(eye, dir, R);
/* copy stuff sothat we can use it on the fragment shader, "discard" is only allowed in fragment shader,
so the rest has to be computed in fragment shader */
eOutput = e;
eyeOutput = eye;
dirOutput = dir;
lOutput = dir;
}
#endif
#ifdef FRAGMENT
uniform sampler2D _MainTex;
varying vec2 TextureCoordinate;
uniform vec4 _LC;
varying float LightIntensity;
/* transient port */
varying vec3 eyeOutput;
varying vec3 dirOutput;
varying vec3 lOutput;
varying vec2 eOutput;
void main()
{
/* real fragment */
if (eOutput.x > eOutput.y) {
//discard;
}
vec2 f = ray_vs_sphere(eyeOutput, dirOutput, R_INNER);
vec2 e = eOutput;
e.y = min(e.y, f.x);
vec3 I = in_scatter(eyeOutput, dirOutput, eOutput, lOutput);
gl_FragColor = vec4(I, 1.0);
/*vec4 c2;
c2.x = 1.0;
c2.y = 1.0;
c2.z = 0.0;
c2.w = 1.0f;
gl_FragColor = c2;*/
//gl_FragColor = c;
}
#endif
ENDGLSL
}
}
}
Każda pomoc jest doceniana, przepraszam za długi post i wyjaśnienia.
Edycja: Właśnie dowiedziałem się, że promień gofra ma wpływ na rzeczy, kula ze skalą 2.0 w każdym kierunku daje o wiele lepszy wynik. Jednak zdjęcie jest wciąż całkowicie niezależne od kąta patrzenia kamery i jakichkolwiek świateł, to nie jest tak blisko wersji shaderlab.
wielkie pytanie ... – Fattie
@OP Właśnie wskakuję do shaderów. Czy w końcu rozwiązałeś ten problem? – Programmer
@Programmer Nie nad tym pracowaliśmy od zawsze - każdy może swobodnie próbować implementować sugestie w odpowiedzi. –