首页 > 解决方案 > 为什么 Unity 的 WebGL 构建不允许我的着色器使用条件开关来渲染光线行进对象

问题描述

我一直在尝试制作一个着色器,它可以让光线行进在没有重型显卡的设备上发生。我正在使用 unity3d,但在一个问题上停留了大约 2 周。

我现在拥有的着色器非常简单,并且可以在不同平台上提供良好的 fps。它甚至在 WebGL 中运行得非常快(这一直是一场噩梦),但最后一个难题是允许用户选择不同的光线行进形状。

我一辈子都想不通为什么,第 94 行的简单开关在 WebGL 中不起作用。

我试过了:

  1. 将其重写为 if/else 语句
  2. 三元运算符
  3. 试图通过线性逻辑运算符确定正确的符号距离
  4. 基于比较的数学,以避免与条件分支。

我现在很想使用诸如 step 或 lerp 之类的函数的数学版本,但它们的实现有时也涉及似乎分支着色器的条件或逻辑运算符。WebGL 只是忽略我尝试的所有内容,它只会在 Web 控制台中打印(错误:制服太多)。

就好像它总是知道我在做什么(◕︵◕)

我也在尝试安装其他 Webgl 调试控制台,例如 spector.js,但我没有从中找到任何重要的线索。

我是着色器编程的新手,所以真的不知道调试它的最佳方法,并且想知道社区中是否有人知道出路。

Properties
{
    _MainTex ("Texture", 2D) = "white" { }
}
SubShader
{
    // No culling or depth
    Cull Off ZWrite Off ZTest Always

    Pass
    {
        CGPROGRAM
        
        #pragma vertex vert
        #pragma fragment frag
        
        #include "UnityCG.cginc"
        #include "SignedDistanceFunctions.cginc"

        sampler2D _MainTex;
        float4x4 _FrustrumCornersES;
        float4 _TexelSize;
        float4x4 _CameraInvViewMatrix;
        float _Scale;
        float _Size;
        float4 _LightDir;
        sampler2D _CameraDepthTexture;
        float _SphereScale;
        float _SmoothUnion;

        //Shape Arrays 
        int _ShapeCount;
        float4 shapePosition[256];
        float4x4 shapeToLocal[256];
        float4 shapeExtent[256];
        float shapeType[256];
        float4 shapeColor[256];
        
        struct appdata
        {
            float4 vertex: POSITION;
            float2 uv: TEXCOORD0;
        };
        
        struct v2f
        {
            float2 uv: TEXCOORD0;
            float4 vertex: SV_POSITION;
            float3 ray: TEXCOORD1;
        };


        // How many times each ray is marched
        // Higher values give higher resolution 
        // (and potentially longer draw distances) 
        // but lower performance
        static const int maxSteps = 60;

        //How close does a ray have to get to be consider a hit
        //Higher values give a sharper definition of shape 
        // but lower performance
        static const float epsilon = 0.03;

        // The maximum distance we want a ray to be from 
        // the nearest surface before giving up
        //Higher values give a longer draw distance but 
        // lower performance
        static const float maxDist = 10;

        // Utility function to find out 
        // when two values are equal
        // Return 1 if equal, 0 if not.
        float when_eq(float x, float y) {
            return 1.0 - abs(sign(x - y));
        }
        
        //Get the specific shape of a raymarch object
        float4 GetShape(float3 p, int index)
        {
            float3 position = mul(shapeToLocal[index], float4((p),1.0));

            float3 col = float3(shapeColor[index].x,
                                shapeColor[index].y,
                                shapeColor[index].z);

            float dst = 0;


            switch (shapeType[index]) 
            {
                case 0:
                    dst = sdSphere(shapePosition[index] - p,
                                    length(float3(shapeExtent[index].x,
                                    shapeExtent[index].y,
                                    shapeExtent[index].z)));
                    break;
                case 1:
                    dst = sdBox(position, 
                                float3(shapeExtent[index].x,
                                shapeExtent[index].y,
                                shapeExtent[index].z));
                    break;
                case 2:
                    dst = sdTorus(position, 
                                length(shapeExtent[index].x), 
                                length(shapeExtent[index].z));
                    break;
                case 3:
                    dst = sdCone(position, 
                                float2(shapeExtent[index].x,
                                shapeExtent[index].z), 
                                shapeExtent[index].y);
                    break;
                case 4:
                    dst = sdCylinder(position, 
                                    length(float2(shapeExtent[index].x,
                                    shapeExtent[index].z)),
                                    shapeExtent[index].y);
                    break;
            }

            return float4(col, dst);
        }

        // Map out Signed distances by looping 
        // over all shapes we feed the shader           
        float4 map(float3 p)
        {
            float4 dist = GetShape(p,0);

            [unroll(4)]  
            for (int i = 0; i < _ShapeCount; i++){
                dist = opSmoothUnion(dist, 
                                    GetShape(p,i), 
                                    _SmoothUnion);
            }
            
            return dist;
        }

        //Get normals given a point         
        float3 calcNormal(float3 p)
        {
            float x = map(float3(p.x + epsilon, p.y, p.z)).w 
                    - map(float3(p.x - epsilon, p.y, p.z)).w;
            float y = map(float3(p.x, p.y + epsilon, p.z)).w 
                    - map(float3(p.x, p.y - epsilon, p.z)).w;
            float z = map(float3(p.x, p.y, p.z + epsilon)).w 
                    - map(float3(p.x, p.y, p.z - epsilon)).w;

            return normalize(float3(x,y,z));
        }

        // Actual Raymarching function,
        // returns a color for different points 
        // where our rays hit objects.
        fixed4 raymarch(float3 rayOrigin, float3 rayDirection, float s)
        {
            fixed4 col = fixed4(0, 0, 0, 0);
            
            const int timeStep = 100;
            float travelled = 0;
            for (int i = 0; i < timeStep; i ++)
            {
                float3 position = rayOrigin + rayDirection * travelled;
                float4 surf = map(position);
                
                if (travelled > maxDist || travelled >= s)
                // if (travelled >= s)
                {
                    col = fixed4(0, 0, 0, 0);
                    break;
                }
                
                if(surf.w < 0.03)
                {
                    float3 n = calcNormal(position);
                    col = fixed4(surf.rgb * dot(-_LightDir.xyz, n).rrr, 1);
                    break;
                }
                
                travelled += surf.w;
            }
            return col;
        }

        //Vertex            
        v2f vert(appdata v)
        {
            v2f o;
            
            half index = v.vertex.z;
            v.vertex.z = 0;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.uv = v.uv.xy;
            
            // #if UNITY_UV_STARTS_AT_TOP
            //  if(_TexelSize.y < 0)
            //      o.uv.y = 1 - o.uv.y;
            // #endif
            
            o.ray = _FrustrumCornersES[(int)index].xyz;
            //Normalize on the Z axis - viewspace position
            o.ray /= abs(o.ray.z);
            
            o.ray = mul(_CameraInvViewMatrix, o.ray);
            
            return o;
        }
        
        //Fragment
        fixed4 frag(v2f i): SV_Target
        {
            float3 rayDir = normalize(i.ray.xyz);
            float3 rayOrigin = _WorldSpaceCameraPos;
            
            float2 duv = i.uv;
            if (_TexelSize.y < 0)
                duv.y = 1 - duv.y;
            
            float depth = LinearEyeDepth(tex2D(_CameraDepthTexture, duv).r);
            depth *= length(i.ray.xyz);
            
            fixed3 col = tex2D(_MainTex, i.uv);
            fixed4 add = raymarch(rayOrigin, rayDir, depth);
            
            return (fixed4(col * (1.0 - add.w) + add.xyz * add.w, 1.0) + 0.2);
        }
        ENDCG
        
    }
}

} 吨

标签: webglunity-webglunity3d-shadersraymarching

解决方案


推荐阅读