You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何将Surface Shader转为Vertex/Fragment着色器?含示例代码转换需求

Alright, let's break down how to convert a Unity Surface Shader to a Vertex/Fragment Shader, then walk through converting your specific "Diamond Opaque Test" shader step by step.

Core Concepts: Surface Shader vs Vertex/Fragment Shader

Surface Shaders are Unity's high-level abstraction that automatically handles multiple rendering passes (like forward/deferred lighting) and simplifies lighting model implementation. When converting to a Vertex/Fragment Shader, you'll need to manually handle:

  • Vertex transformation (model → world → clip space)
  • Calculating vectors needed for lighting (normals, view direction, light direction)
  • Implementing the lighting model (BlinnPhong in your case)
  • Handling extra effects (rim lighting, cubemap reflection here)
  • Adding shadow casting/receiving logic

Converted Vertex/Fragment Shader for Your Diamond Effect

Here's the full converted code that matches the behavior of your original Surface Shader:

Shader "Custom/Diamond Opaque Test" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _SpecColor ("Specular Color", Color) = (0.5,0.5,0.5,1)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
        _RimPower ("Rim Power", Range(0,8.0)) = 3.0
        _ReflectColor ("Reflection Color", Color) = (1,1,1,0.5)
        [NoScaleOffset] _Cube ("Cubemap", CUBE) = "" {}
    }

    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 200

        // Forward base pass for main lighting
        Pass {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase // Handle forward lighting variants

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            // Expose properties to CG code
            fixed4 _Color;
            fixed4 _SpecColor;
            float _Shininess;
            float _RimPower;
            fixed4 _ReflectColor;
            samplerCUBE _Cube;

            // Vertex input structure: model-space position, normal, UV
            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            // Data passed from vertex to fragment shader
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldPos : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float3 worldViewDir : TEXCOORD2;
                float3 worldRefl : TEXCOORD3;
                SHADOW_COORDS(4) // Shadow coordinates
            };

            // Vertex shader
            v2f vert (appdata v) {
                v2f o;
                // Transform vertex to clip space
                o.pos = UnityObjectToClipPos(v.vertex);
                // Transform vertex to world space
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                // Transform normal to world space (handles non-uniform scale)
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                // Calculate world-space view direction
                o.worldViewDir = normalize(_WorldSpaceCameraPos - o.worldPos);
                // Calculate world-space reflection direction for cubemap
                o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);

                // Pass shadow data
                TRANSFER_SHADOW(o);
                return o;
            }

            // Fragment shader
            fixed4 frag (v2f i) : SV_Target {
                // Normalize vectors to avoid interpolation artifacts
                float3 worldNormal = normalize(i.worldNormal);
                float3 worldViewDir = normalize(i.worldViewDir);
                // Get main light direction
                float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                // BlinnPhong diffuse calculation
                float NdotL = saturate(dot(worldNormal, worldLightDir));
                fixed3 diffuse = _Color.rgb * _LightColor0.rgb * NdotL;

                // BlinnPhong specular calculation
                float3 halfDir = normalize(worldLightDir + worldViewDir);
                float NdotH = saturate(dot(worldNormal, halfDir));
                // Surface Shader's _Shininess (0-1) maps to pow exponent 0-128
                float spec = pow(NdotH, _Shininess * 128);
                fixed3 specular = _SpecColor.rgb * _LightColor0.rgb * spec;

                // Rim lighting effect
                float rim = 1 - saturate(dot(worldViewDir, worldNormal));
                fixed3 rimColor = _SpecColor.rgb * pow(rim, _RimPower);

                // Cubemap reflection sampling
                fixed3 reflection = texCUBE(_Cube, i.worldRefl).rgb * _ReflectColor.rgb;

                // Shadow attenuation
                fixed shadow = SHADOW_ATTENUATION(i);

                // Combine all color components + ambient light
                fixed3 finalColor = diffuse + specular + rimColor + reflection + UNITY_LIGHTMODEL_AMBIENT.rgb;
                finalColor *= shadow; // Apply shadow darkening

                return fixed4(finalColor, _Color.a);
            }
            ENDCG
        }

        // Shadow caster pass (matches Surface Shader behavior)
        Pass {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster

            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f {
                V2F_SHADOW_CASTER;
            };

            v2f vert (appdata v) {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

Key Details Matching the Original Surface Shader

  • Lighting Model: We manually implemented BlinnPhong to match the #pragma surface surf BlinnPhong directive, including diffuse and specular calculations.
  • Rim Lighting: The rim effect is calculated the same way as in the Surface Shader, using the view direction and normal dot product.
  • Cubemap Reflection: We sample the cube map using the world-space reflection direction, just like the worldRefl input in the Surface Shader's Input struct.
  • Shadows: Added a ShadowCaster pass and used Unity's shadow macros to handle shadow receiving and casting, which the Surface Shader does automatically.
  • Shader Variants: The #pragma multi_compile_fwdbase and #pragma multi_compile_shadowcaster directives ensure compatibility with different lighting setups, just like the Surface Shader's automatic variant generation.

内容的提问来源于stack exchange,提问作者user1105430

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:12:20