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 BlinnPhongdirective, 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
worldReflinput in the Surface Shader'sInputstruct. - Shadows: Added a
ShadowCasterpass and used Unity's shadow macros to handle shadow receiving and casting, which the Surface Shader does automatically. - Shader Variants: The
#pragma multi_compile_fwdbaseand#pragma multi_compile_shadowcasterdirectives ensure compatibility with different lighting setups, just like the Surface Shader's automatic variant generation.
内容的提问来源于stack exchange,提问作者user1105430
相关产品推荐
相关产品推荐

