2016-02-06 14 views
7

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)

unity shader comparison

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.

status2

+1

wielkie pytanie ... – Fattie

+0

@OP Właśnie wskakuję do shaderów. Czy w końcu rozwiązałeś ten problem? – Programmer

+0

@Programmer Nie nad tym pracowaliśmy od zawsze - każdy może swobodnie próbować implementować sugestie w odpowiedzi. –

Odpowiedz

0

To wyglądać jak próbujesz renderowanie 2D tekstury nad kuli. Ma trochę inne podejście. Do tego, co próbujesz zrobić, zastosowałbym moduł cieniujący na płaszczyźnie skrzyżowanej ze sferą.

W ogólnym celu spójrz na this article, pokazując, jak przekonwertować shaderToy na Unity3D.

Nie

jest kilka kroków, że zawarte tutaj:

  • Wymień iGlobalTime wejście shaderów („shader czas odtwarzania w sekundach”) z _Time.y
  • („Zamień iResolution.xy rozdzielczość rzutni w pikselach”) z _ScreenParams.xy
  • wymienić vec2 typów o float2, mat2 z float2x2 itp
  • wymienić vec3 (1) konstruktory skrótów, w którym wszystkie elementy mają tę samą wartość wyraźnych float3 (1,1,1)
  • Wymień Texture2D na Tex2D
  • Zamień atan (x, y) na atan2 (y, x) < - Zapamiętaj uporządkowanie parametrów!
  • wymienić mieszankę() z lerp()
  • wymiana * = z mul()
  • usunąć trzeci (bias) parametr z Texture2D wyszukiwań
  • mainImage (na vec4 fragColor w vec2 fragCoord) jest wówczas moduł fragment funkcja, odpowiednik float4 mainImage (float2 fragCoord: SV_POSITION): SV_Target
  • Współrzędne UV w GLSL mają 0 u góry i rosną w dół, w HLSL 0 u dołu i rosną w górę, więc możesz potrzebować użyć uv.y = 1 - uv.y w pewnym momencie.

O to pytanie:

Tags{ "Queue" = "Geometry" } //Is this even the right queue? 

Kolejka odwołuje kolejność będzie renderowany, geometria jest jednym z pierwszych, jeśli chcesz, Shader działa ponad wszystko, czego można wykorzystać nakładki na przykład. Ten temat to covered here.

  • Tło - ta kolejka renderowania jest renderowana przed innymi. Jest używany do skyboxów i tym podobnych.
  • Geometria (domyślnie) - jest używana w przypadku większości obiektów.Nieprzezroczysta geometria używa tej kolejki.
  • AlphaTest - geometria testowana alfa używa tej kolejki. Jest to osobna kolejka od - Geometry jeden, ponieważ bardziej wydajne jest renderowanie obiektów z testem alfa po narysowaniu wszystkich brył.
  • Przezroczysty - ta kolejka renderowania jest renderowana po Geometrii i AlphaTest w kolejności odwrotnej. Wszystko, co łączy się z alfa (tj. Shadery, które nie piszą do bufora głębi), powinno iść tutaj (szkło, efekty cząsteczkowe).
  • Nakładka - ta kolejka renderowania jest przeznaczona do nakładania efektów. Wszystko, co jest ostatnio oddane, powinno znaleźć się tutaj (np. Flary obiektywu).