Unity

    xiaoxiao2023-10-24  198

    Unity支持多种类型的渲染路径:前向渲染路径,延迟渲染路径,顶点照明渲染路径(好像没了,我也没用过)。

    在项目设置里可以设置整体的渲染路径,摄像机里也可以设置是否采用项目设置里的渲染路径。 这里列出的都是在Pass Tags LightMode 里的标签,看情况加。

    先说说关于前向渲染:前向渲染里有三种光照方式:逐顶点,逐像素,球谐函数(SH)处理,。光源的渲染模式是设置该光源是否重要,根据是否重要在前向渲染种决定是使用哪一种光照方式。

    前向渲染是两个Pass,一个是BasePass,就是计算平行光,环境光,自发光,光照纹理,平行光的阴影 ,简单理解就是 逐像素计算平行光,其他的都是逐顶点的。 在BasePass里的平行光默认是支持阴影的计算,对于环境光和自发光,都是放在了这个pass计算,这两个是因为只要计算一次,而不是在后面的AddPass里再计算,造成OverDraw。

    第二个pass是Additional Pass,我简称为addpass,这个pass就是补充了第一个pass的不足,去计算之前没有逐像素计算的一些灯光(就是那些你认为影响比较大的灯光),出现一个逐像素的灯光,这个pass就被调用一次。

    配套使用的编译指令:BasePass “LightMode”=“FordwardBase” #pragma multi_compile_fwdbase

    AdditionalPass “LightMode”="FordwardAdd Blend One One(用One One,是与上一个pass中得出的计算结果进行线性减淡的叠加(这在我之前的02笔记里提到了这种混合方式),不加就会覆盖上一个计算) #pragma multi_compile_fwdadd 下面上个代码: Shader “Unlit/Forward Rendering” { Properties { _Diffuse(“Diffuse”, Color) = (1, 1, 1, 1) _Specular(“Specular”, Color) = (1, 1, 1, 1) _Gloss(“Gloss”, Range(8.0, 256)) = 20 } SubShader { Tags { “RenderType” = “Opaque” } Pass { // Pass for ambient light & first pixel light (directional light) Tags { “LightMode” = “ForwardBase” } CGPROGRAM // Apparently need to add this declaration // #pragma multi_compile_fwdbase #pragma vertex vert #pragma fragment frag #include “Lighting.cginc”//因为要访问到灯光的颜色信息 fixed4 _Diffuse; fixed4 _Specular; float _Gloss; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } fixed4 frag(v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir)); fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);//之前提到的Blinn-Phone return fixed4(ambient + (diffuse + specular), 1.0); } ENDCG } Pass { // Pass for other pixel lights Tags { “LightMode” = “ForwardAdd” } Blend One One CGPROGRAM // Apparently need to add this declaration #pragma multi_compile_fwdadd_fullshadows #pragma vertex vert #pragma fragment frag #include “Lighting.cginc” #include “AutoLight.cginc” fixed4 _Diffuse; fixed4 _Specular; float _Gloss; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } // 不在需要环境光的叠加,在BasePass中已经计算过环境光的影响了,多次叠加会导致色彩异常 fixed4 frag(v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); #ifdef USING_DIRECTIONAL_LIGHT // 平行光直接可获取到 顶点到灯光的方向,上面的列表里有这函数 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); #else // 如果是其他类型的灯光还需要进一步手动计算 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif // 1.漫反射 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir)); fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); fixed3 halfDir = normalize(worldLightDir + viewDir); // 2.高光反射 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss); // 3.计算光照衰减 attenuation,这里是根据灯光类型不行,采用了不同的计算(好像还有个Unity自己的自动灯光计算,以后加) #ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0;//平行光没有衰减 #else #if defined (POINT)//点光源 // 计算顶点世界坐标到光源的 光源空间位置 【注意:是unity_WorldToLight】 float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;//unity_WorldToLight也就是_LightMatrix0,坐标转换到光源空间 // 用了_LightTexture0用来计算光照衰减,避免了复杂的数学计算。(0,0)表示最近点的衰减,(1,1)表示最远点的衰减如果用 //了cookie,需要使用_LightTextureB0来计算 fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;//使用了这个坐标的模的平方来做衰减,因为 //点光源的衰减是平方衰减,用这方法避免了开方操作,UNITY_ATTEN_CHANNEL得到衰减纹理中衰减值分量 #elif defined (SPOT)//聚光灯类型 float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)); fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;//也是一样的,聚光灯的计算,比较复杂,我自己都不想看 #else fixed atten = 1.0; #endif #endif return fixed4((diffuse + specular) * atten, 1.0); } ENDCG } } //FallBack “Specular” }

    关于延迟渲染:也是两个Pass,第一个Pass用于渲染G缓冲,把物体的漫反射颜色,高光反射颜色,平滑度,法线,自发光和深度等信息渲染到屏幕空间的G缓冲区中。对于整个物体来说,这个Pass只会执行一次。说白了,就是一种预处理,我先把计算光照要用到的玩意都先给你了,然后下一步就是你去干光照吧。

    第二个Pass开始真正的计算 //https://www.cnblogs.com/wangchengfeng/p/3440097.html 延迟渲染的

    总结:缺点比较多,对于计算量大的时候可以使用。

    ShadowMap:就是把摄像机的位置放在与光源重合的位置上,看的到的没阴影,看不到的地方出现阴影。所以再前向渲染中,这也算是一种深度图吧,记录了看到的东西(距离最近的表面位置,即深度信息)

    Pass:“LightMode”=“ShadowCaster” (中文是阴影投射?物体上勾选的castshadow和这玩意有关系嘛?) 如果写的pass里没有,会再fallback里找这玩意,所以有时候写了fallback diffuse就有阴影了,不过用的是fallback里的,如果你有裁剪啊,透明度等变化,这里还是要重新写一下,不然阴影就不对了。

    这里说一下啊,投射阴影和接受阴影是两码事,要接受阴影,就要对阴影映射纹理进行采样,混合光照结果产生阴影。 而投射阴影就要把这物体加入到光源的阴影映射纹理中!这操作就是执行shadowcaster这个pass。

    投射阴影四部曲:1.LightMode设置为ShadowCaster 2.编译指令#pragma multi_compile_shadowcaster 3.v2f结构体定义V2F_SHADOW_CASTER 4.顶点着色器:TRANSFER_SHADOW_CASTER_NORMALOFFSET(o); 5.表面着色器:SHADOW_CASTER_FRAGMENT(i); 接受阴影四部曲:1.在前向渲染的pass里添加程序集 #include"AutoLight.cginc" 2.v2f结构体里添加内置宏SHADOW_COORDS(n),n是一个数量的序号//声明个变量的 3.在顶点着色器里添加TRANSFER_SHADOW(o);//转换空间的 4.在片元着色器里也使用内置宏SHADOW_ATTENUATION(i) //采样的 接受阴影补充:将阴影和光线衰减统一,使用SHADOW_LIGHT_ATTENUATION(atten,i,i.worldPos) atten也就是衰减因子,都由unity计算好了,直接用就行了

    最新回复(0)