2016-02-06 12 views
7

Ich versuche gerade, einen Shadertoy.com-Shader (Atmospheric Scattering Sample, interaktive Demo mit Code) in Unity zu portieren. Der Shader ist in GLSL geschrieben und ich muss den Editor mit C:\Program Files\Unity\Editor>Unity.exe -force-opengl starten, damit er den Shader rendert (ansonsten wird ein "Dieser Shader kann nicht auf dieser GPU ausgeführt werden" angezeigt), aber das ist momentan kein Problem. Das Problem besteht darin, diesen Shader auf Unity zu portieren.Probleme bei der Portierung eines GLSL-Shadertoy-Shaders auf Einheit

Die Funktionen für die Streuung usw. sind alle identisch und "runable" in meinem portierten Shader, die einzige Sache ist, dass die mainImage() Funktionen die Kamera, Lichtrichtungen und Strahlrichtung selbst verwaltet. Dies muss natürlich so geändert werden, dass die Kameraposition, die Blickrichtung und die Lichtquellen und Richtungen von Unity verwendet werden.

Die Hauptfunktion der ursprünglichen sieht wie folgt aus:

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); 
} 

Ich habe durch die Dokumentation dieser Funktion lesen und wie wird es Arbeit bei https://www.shadertoy.com/howto soll.

Bild Shadern implementieren die Mainimage() Funktion, um das Verfahren Bilder durch Berechnen eine Farbe für jedes Pixel zu erzeugen. Diese Funktion wird voraussichtlich einmal pro Pixel aufgerufen, und es ist Verantwortlichkeit der Host-Anwendung, um die richtigen Eingaben zu es und holen Sie sich die Ausgabefarbe von ihm und weisen Sie es dem Bildschirm Pixel. Der Prototyp ist:

void mainImage (aus vec4 fragColor, in vec2 fragCoord);

Dabei enthält fragCoord die Pixelkoordinaten, für die der Shader eine Farbe berechnen muss. Die Koordinaten sind in Pixeleinheiten im Bereich von 0,5 bis zu einer Auflösung von 0,5 über der Renderingoberfläche, wobei die Auflösung über die iResolution-Uniform (siehe unten) an den Shader übergeben wird.

Die resultierende Farbe wird in fragColor als vier Komponenten Vektor gesammelt, von denen der letzte vom Client ignoriert wird. Das Ergebnis ist gesammelt als eine "out" Variable in Voraussicht der zukünftigen Hinzufügung von mehrere Renderziele.

So in dieser Funktion gibt es Hinweise auf iGlobalTime die Kamera mit der Zeit und Verweisen auf die iResolution für die Auflösung drehen zu machen. Ich habe den Shader in einen Unity-Shader eingebettet und versucht, die dir, eye und l so zu reparieren und zu verkabeln, dass es mit Unity funktioniert, aber ich bin völlig festgefahren. Ich bekomme eine Art von Bild, das „verwandt“ auf den ursprünglichen Shader aussieht: (Top ist originell, Buttom die aktuelle Einheit Zustand)

unity shader comparison

Ich bin kein Shader profesional, ich kenne nur einige Grundlagen von OpenGL, aber zum größten Teil schreibe ich Spiellogik in C#, also konnte ich mir nur andere Shader-Beispiele ansehen und schauen, wie ich die Daten über Kamera, Lichtquellen usw. in diesem Code bekommen konnte, aber wie du kannst Sehen Sie, nichts geht wirklich.

Ich habe den Skelton-Code für den Shader von https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Specular_Highlights und einige Vektoren von http://forum.unity3d.com/threads/glsl-shader.39629/ kopiert.

Ich hoffe, dass jemand mir in eine Richtung zeigen kann, wie man diesen Shader repariert/richtig an die Einheit portiert. Unten ist der aktuelle Shader-Code, alles, was Sie tun müssen, um es zu reproduzieren, ist einen neuen Shader in einem leeren Projekt zu erstellen, diesen Code hinein zu kopieren, ein neues Material zu erstellen, den Shader diesem Material zuzuweisen, dann eine Kugel hinzuzufügen und dieses Material hinzuzufügen darauf und fügen Sie ein gerichtetes Licht hinzu.

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 
    } 
    } 
} 

Jede Hilfe wird geschätzt, Entschuldigung für die langen Post und Erklärungen.

Edit: Ich habe gerade herausgefunden, dass der Radius der Sphäre einen Einfluss auf das Zeug hat, eine Kugel mit der Skalierung 2.0 in jeder Richtung gibt ein viel besseres Ergebnis. Jedoch ist das Bild immer noch völlig unabhängig vom Blickwinkel der Kamera und von irgendwelchen Lichtern, dies ist bei weitem nicht die Shaderlab-Version.

status2

+1

große Frage ... – Fattie

+0

@OP Ich bin nur in Shadern springen. Hast du das Problem gelöst? – Programmer

+0

@Programmer Seitdem habe ich noch nicht daran gearbeitet - jeder kann sich frei fühlen, die Vorschläge in der Antwort zu implementieren. –

Antwort

0

Es ist aussehen wie Sie eine 2D-Textur über eine Kugel zu machen versuchen. Es hat einen anderen Ansatz. Für das, was Sie tun möchten, würde ich den Shader über ein mit der Kugel gekreuztes Flugzeug anwenden.

Für allgemeine Zwecke, sehen Sie this article zeigt, wie ShaderToy zu Unity3D konvertieren.

Es gibt einige Schritte, die ich hier enthalten:

  • iGlobalTime Shader-Eingabe ersetzen („in Sekunden Shader-Wiedergabezeit“) mit _Time.y
  • ersetzen iResolution.xy („Ansichtsfenster Auflösung in Pixel“ mit _ScreenParams.xy) Ersetzen
  • vec2 Typen mit float2, MAT2 mit float2x2 usw.
  • vec3 Ersetzen (1), in dem alle Elemente Verknüpfung Konstruktoren gleichen Wert mit explizitem float3 haben (1,1,1)
  • Textur2D durch Tex2D ersetzen
  • Ersetzen Sie atan (x, y) durch atan2 (y, x) < - Beachten Sie die Parameterreihenfolge!
  • Ersetzen mix() mit Lerp()
  • mit mul Ersetzen * =()
  • dritte (bias) Parameter aus Texture2D Lookups
  • Mainimage (out vec4 fragColor, in vec2 fragCoord) die Fragment-Shader entfernen Funktion, äquivalent zu float4 mainImage (float2 fragCoord: SV_POSITION): SV_Target
  • UV-Koordinaten in GLSL haben 0 oben und steigen nach unten, in HLSL 0 ist unten und steigt nach oben, also müssen Sie uv.y verwenden = 1 - uv.y irgendwann.

Über diese Frage:

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

Queue verweist auf die Bestellung wird es gemacht werden, ist Geometrie einer der ersten von, wenn man Sie über alles Shader laufen wollen Sie Overlay zum Beispiel nutzen könnten. Dieses Thema lautet covered here.

  • Hintergrund - diese Render-Warteschlange wird vor allen anderen gerendert. Es wird für Skyboxen und dergleichen verwendet.
  • Geometrie (Standard) - Dies wird für die meisten Objekte verwendet.Opake Geometrie verwendet diese Warteschlange.
  • AlphaTest - Alpha getestete Geometrie verwendet diese Warteschlange. Es ist eine separate Warteschlange von - Geometrie eins, da es effizienter ist, Alpha-getestete Objekte zu rendern, nachdem alle festen gezeichnet wurden.
  • Transparent - Diese Renderwarteschlange wird nach Geometrie und AlphaTest in umgekehrter Reihenfolge gerendert. Alles Alpha-Blended (d. H. Shader, die nicht in den Tiefenpuffer schreiben) sollte hier gehen (Glas, Partikeleffekte).
  • Überlagerung - Diese Render-Warteschlange ist für Overlay-Effekte gedacht. Alles, was zuletzt gerendert wurde, sollte hier gehen (z. B. Lens Flares).
Verwandte Themen