Surface Shader学习笔记

xiaoxiao2021-02-28  22

表面着色器的实质就是在顶点、片元着色器上的一层抽象封装,unity自动在背后帮我们处理渲染路径,使用的光源模型等。 但能用表面着色器实现的shader顶点片元着色器都能实现,反之不成立。 https://docs.unity3d.com/Manual/SL-SurfaceShaders.html 表面着色器的代码必须包含在Subshader中,表面着色器不存在Pass因为着色代码在多通道中编译然后自动生成多个Pass。 一、 #pragma surface surfaceFunction lightModel [optionalparams] surfaceFunction(指明使用的表面函数): void surf(Input IN, inout SurfaceOutput o) void surf(Input IN, inout SurfaceOutputStandard o)  基于PBR void surf(Inout IN, inout SurfaceOutputStandardSpecular o)  基于PBR lightModel(指明使用的光照模型): 内置不基于物理的: Lambert,BlinnPhong 内置基于物理的: Standard,StandardSpecular 自定义光照模型 [optionalparams](可选参数): 透明度混合与透明度测试 alpha or alpha:auto  为简单光照选择褪色透明度(等同于alpha:fade) ,以及基于物理照明的预乘透明度(等同于alpha:premul)  alpha:blend              开启透明度混合 alpha:fade                开启传统渐变透明 alpha:premul            开启预乘a透明度 alphatest:VariableName   根据VariableName的变量来控制透明度混合和透明度测试,VariableName是一个float型的变量,剔除不满足条件的片元,此时往往需要用到addshadow来生成正确阴影投射的Pass keepalpha                 默认不透明表面着色器将1写入A通道,不管alpha输出值以及光照函数的返回值 decal:add                  对其他表面上的物体使用additive blending decal:blend               对其他表面上的物体使用alpha blending 自定义修改函数 vertex:VertexFunction         顶点修改函数,用于修改计算顶点位置、信息等 finalcolor:ColorFunction      最终颜色修改函数 finalgbuffer:ColorFunction  自定义延迟路径,用于更改gbuffer finalprepass:ColorFunction  自定义预处理路径 阴影 addshadow                  生成一个阴影投射的Pass,为一些使用了顶点动画、透明度测试的物体产生正确的阴影 fullforwardshadows     支持前向渲染路径中所有光源类型的阴影,shader默认只支持最重要平行光的阴影,添加该参数可以支持点光源或聚光灯的阴影效果  tessellate:TessFunction 使用DX11 GPU曲面细分 控制代码生成(表面着色器默认处理所有坑能的光照、阴影、光照烘培,可手动调整跳过一些不必要的加载提升性能) exclude_path:deferred, exclude_path:forward, exclude_path:prepass  不为某个渲染路径生成代码 noshadow           禁用阴影 noambient          不应用任何环境光以及光照探针 novertexlights     在前向渲染路径中不应用任何逐顶点光照及光照探针 nolightmap         不应用任何光照烘培 nodynlightmap   不应用实时GI nodirlightmap     不应用directional lightmaps nofog                 不应用任何雾效 nometa           生成meta这个Pass(that’s used by lightmapping & dynamic global illumination to extract surface information) noforwardadd    不应用前向渲染中所有的additive pass,使得shader只支持一个重要平行光,其他光用逐顶点/SH光源计算光照影响,使shader更精简 nolppv                不应用光照探针代理Light Probe Proxy Volume(LPPV) noshadowmask   不应用Shadowmask 其他 softvegetation      只有当Soft Vegetation(软植被)开启时该shader才被渲染 interpolateview     在顶点而不是片元着色器中计算 view direction并插值,需多使用一张纹理插值器,提升渲染速度 halfasview           Pass half-direction vector into the lighting function instead of view-direction. Half-direction will be computed and normalized per vertex. This is faster, but not entirely correct. dualforward         在前向渲染中使用dual lightmaps dithercrossfade     使表面着色器支持 dithering effects 二、Input 包含表面属性数据来源,作为表面函数的输入结构体,顶点修改函数的输出结构体 其中的采样坐标必须以uv为前缀,如uv_MainTex(uv2也可,表明使用次级纹理坐标集合) 各个变量往往由Unity自动准备好,直接在表面函数中使用即可,但如自定义了顶点修改函数用Input作为输出时需在里面自定义相应变量 float3 viewDir                             包含视角方向 float4 with COLOR semantic       包含插值后的逐顶点颜色 float4 screenPos             包含屏幕空间坐标,用于反射、屏幕特效等,不支持 GrabPass ,需自己用ComputeGrabScreenPos计算UV  float3 worldPos                          包含世界空间位置 float3 worldRefl                          包含世界空间下反射方向,前提是没有修改表面法线o.Normal float3 worldNormal                    包含世界空间下法线方向,前提是没有修改表面法线o.Normal float3 worldRefl; INTERNAL_DATA          如果修改了表面法线o.Normal,需要使用该变量告诉Unity要基于修改后的法线计算世界空间下的反射方向。用WorldReflectionVector (IN, o.Normal)得到世界空间下的反射方向。 float3 worldNormal; INTERNAL_DATA    如果修改了表面法线o.Normal,需要使用该变量告诉Unity要基于修改后的法线计算世界空间下的法线方向。用WorldReflectionVector (IN, o.Normal)得到世界空间下的法线方向。 三、SurfaceOutput 作为表面函数的输出,作为光照函数的输入进行各种光照计算 结构体中的变量是提前声明好的,不可增加或减少,若没有赋值则使用默认值 struct SurfaceOutput(非物理的光照模型) {     fixed3 Albedo;  // diffuse color     fixed3 Normal;  // tangent space normal, if written     fixed3 Emission;     half Specular;  // specular power in 0..1 range     fixed Gloss;    // specular intensity     fixed Alpha;    // alpha for transparencies }; struct SurfaceOutputStandard(默认金属工作流程) {     fixed3 Albedo;      // base (diffuse or specular) color     fixed3 Normal;      // tangent space normal, if written     half3 Emission;     half Metallic;      // 0=non-metal, 1=metal     half Smoothness;    // 0=rough, 1=smooth     half Occlusion;     // occlusion (default 1)     fixed Alpha;        // alpha for transparencies }; struct SurfaceOutputStandardSpecular(高光工作流程) {     fixed3 Albedo;      // diffuse color     fixed3 Specular;    // specular color     fixed3 Normal;      // tangent space normal, if written     half3 Emission;     half Smoothness;    // 0=rough, 1=smooth     half Occlusion;     // occlusion (default 1)     fixed Alpha;        // alpha for transparencies }; 其中,Specular为高光反射中指数部分的系数,Gloss为高光反射中强度系数 四、自定义光照模式 half4 LightingName (SurfaceOutput s, half3 lightDir, half atten); 用于表示前向渲染路径中的光照模式,不取决于view direction half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten); 用于表示前向渲染路径中的光照模式,包含view direction half4 LightingName_PrePass (SurfaceOutput s, half4 light); 用于延迟光照路径 half4 LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal); half4 LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, half3 viewDir, bool surfFuncWritesNormal,out half3 specColor); 前者不包含,后者包含view direction,这两个函数会自动处理前向渲染路径和延迟渲染路径 half4 Lighting<Name> (SurfaceOutput s, UnityGI gi); Use this in forward rendering paths for light models that are not dependent on the view direction. half4 Lighting<Name> (SurfaceOutput s, half3 viewDir, UnityGI gi); Use this in forward rendering paths for light models that are dependent on the view direction. half4 Lighting<Name>_Deferred (SurfaceOutput s, UnityGI gi, out half4 outDiffuseOcclusion, out half4 outSpecSmoothness, out half4 outNormal); Use this in deferred lighting paths. half4 Lighting<Name>_PrePass (SurfaceOutput s, half4 light); Use this in light prepass (legacy deferred) lighting paths.

half4 Lighting<Name>_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi);

五、实列

自定义光照模型,之中实现自定义Lambert并采样渐变纹理

用同样的方式在surf函数中计算高光,并采样用于高光的渐变纹理

最后再加上菲涅尔rim,大功告成

下面为核心代码实现:

#pragma lighting ToonRamp exclude_path:prepass half4 LightingToonRamp(SurfaceOutput s, half3 lightDir, half atten) { #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(lightDir); #endif half d = dot(s.Normal, lightDir)*0.5 + 0.5; half3 ramp = tex2D(_Ramp, float2(d,d)).rgb; half4 c; c.rgb = s.Albedo * _LightColor0.rgb * ramp * (atten * 2); c.a = 0; return c; } void surf(Input IN, inout SurfaceOutput o) { half4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; half d = dot(o.Normal, IN.lightDir)*0.5 + _SpecOffset; //_SpecOffset控制高光偏移量 half3 rampS = tex2D(_RampS, float2(d, d)).rgb; //采样高光渐变纹理 float rim = 1 - saturate(dot(IN.viewDir, o.Normal)); //fresnel rim o.Emission = _RimColor.rgb * pow(rim, 1.5); //fresnel rim o.Albedo = (step(_SpecSize, rampS.r)) * rampS * d * _SColor; //计算高光,_SpecSize控制高光大小 o.Alpha = c.a; }

最后得到效果如下:

表面着色器能在我们需要和光打交道的时候发挥出色,但unity隐藏了很多实现的细节,其作为顶点片元着色器更抽象一层的封装需要我们二者兼顾。

转载请注明原文地址: https://www.6miu.com/read-2628461.html

最新回复(0)