赞
踩
随着Unity2019 LTS版本的推出,URP管线已经可以作为基础渲染管线进行商业游戏和应用的开发。
而原有兼容builtin管线的第三方shader和插件默认是不兼容URP管线的,因此需要手动编辑已有shader使之兼容URP管线,甚至某些情况下要扩展URP渲染管线以实现一些特殊功能。
本系列教程会基于URP管线,创建一系列自定义材质,来演示URP管线的使用。
从易到难,主要涉及以下内容:
URP管线和HDRP管线作为官方的SRP可编程管线案例,依赖SRP核心库:com.unity.render-pipeline.core
API | path |
---|---|
D3D11 | Packages/com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl |
GLCore | …/GLCore.hlsl |
GLES2 | …/GLES2.hlsl |
GLES3 | …/GLES3.hlsl |
Metal | …/Metal.hlsl |
Switch | …/Switch.hlsl |
Vulkan | …/Vulkan.hlsl |
上述平台相关的API文件我们不需要直接引用,SRP Core提供了Common.hlsl 文件,根据不同平台会自己引用对应的API文件
例如: 2D纹理定义和采样
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
TEXTURE_2D(_MainTex); // 定义纹理
SAMPLER(sampler_MainTex); // 定义纹理对应的采样器
half4 color=SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,uv); // 纹理采样
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
TransformObjectToWorld(positionOS.xyz);// 将顶点模型空间到世界空间
TransformObjectToHClip(positionOS.xyz);// 将顶点模型空间到齐次裁剪空间
TransformObjectToWorldNormal(normalOS.xyz);// 将法线从模型空间转换到世界空间
TransformObjectToWorldDir(tangentOS.xyz);// 将向量从模型空间转换到世界空间
该库还提供了其他功能函数,都位于Packages/com.unity.render-pipelines.core/ShaderLibrary目录下,但是在URP中我们通常不直接引用核心库下的功能函数,而是通过引用URP提供的库文件,间接引用到核心库下的内容
本节的实例里就引用了如下的库文件,通过该库文件不仅引用到了前面提到的核心库的内容而且引用到URP新增的一些逻辑相关的接口,例如获取主光源的信息
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
Light light=GetMainLight();
float3 lightDir=light.direction;
float3 lightColor=light.color;
渲染效果如图:
完整代码如下:
本教程所有代码使用如下规范:
Shader "tutorial/chapter_1/urp_101" { Properties { _BaseColor("BaseColor",Color)=(1,1,1,1) _Smoothness("Smoothness",Range(0.1,1))=0.5 _SpecularColor("SpecularColor",Color)=(0.2,0.3,0.3,1) _MainTex("Main Tex",2D)="white"{} _NormalMap("Normal Map",2D)="bump"{} } SubShader { Pass { HLSLPROGRAM // 可编程渲染管线中推荐使用HLSL语法,URP和HDRP都是使用的HLSL #pragma vertex vert #pragma fragment frag // 引入URP中预定义好的一些变量,例如变换矩阵,光源等 #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" half4 _BaseColor; half4 _SpecularColor; float _Smoothness; TEXTURE2D(_MainTex); // 声明2D纹理贴图 float4 _MainTex_ST; // 纹理贴图的Scale 和 Transform SAMPLER(sampler_MainTex); // 纹理贴图的采样器 TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); struct app_data { float4 positionOS:POSITION; float3 normalOS:NORMAL; float4 tangentOS:TANGENT; float2 texcoord0:TEXCOORD0; }; struct v2f { float4 positionCS:SV_POSITION; // HLSL中保存光栅化用到的顶点坐标值,必须使用SV_POSITION,不再支持使用POSITION // SV为SystemValue,标识该语义为渲染管线硬件必须的值,后边的SV_Target同理 float4 normalWS:TEXCOORD0; float4 tangentWS:TEXCOORD1; float4 bitangentWS:TEXCOORD2; float2 uv0:TEXCOORD3; }; v2f vert(app_data IN) { v2f o=(v2f)0; //将顶点坐标从模型空间变换的世界空间 float3 positionWS=TransformObjectToWorld(IN.positionOS.xyz); // 将法线从模型空间变换到世界空间 float3 normalWS=TransformObjectToWorldNormal(IN.normalOS); // 将切线从模型空间变换到世界空间 float3 tangentWS=TransformObjectToWorldDir(IN.tangentOS.xyz); // 计算副切线的朝向 float crossSign = (IN.tangentOS.w > 0.0 ? 1.0 : -1.0) * GetOddNegativeScale(); float3 bitangentWS=cross(normalWS,tangentWS)*crossSign; // 副切线 o.tangentWS=float4(tangentWS.xyz,positionWS.x); o.bitangentWS=float4(bitangentWS.xyz,positionWS.y); o.normalWS=float4(normalWS.xyz,positionWS.z); // 计算纹理采样时使用的UV o.uv0=IN.texcoord0.xy*_MainTex_ST.xy+_MainTex_ST.zw; // 将顶点坐标从世界空间变换到齐次裁剪空间 o.positionCS=TransformWorldToHClip(positionWS); // 在顶点着色器里,得到的是齐次裁剪空间的顶点坐标,经过光栅化阶段的齐次除,之后才是真正的裁剪空间 return o; } //使用SV_Target 替换掉之前CG中的Color的写法,用来标记把渲染结果存入RT0 half4 frag(v2f IN):SV_Target { // 采样纹理贴图 half4 baseColor=SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,IN.uv0); // 采样法线贴图并解析出法线空间的法线 float3 normalTS= UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap,sampler_NormalMap,IN.uv0)); float3x3 tangentToWorld=float3x3( normalize(IN.tangentWS.xyz), normalize(IN.bitangentWS.xyz), normalize(IN.normalWS.xyz)); float3 positionWS=float3(IN.tangentWS.w,IN.bitangentWS.w,IN.normalWS.w); float3 normalWS=mul(normalTS,tangentToWorld); Light light=GetMainLight(); // 主光源 float3 lightDir=normalize(light.direction); half3 lightColor=light.color; float NdotL=saturate(dot(normalWS,lightDir)); //获取相机坐标,进而计算观察方向 float3 viewDir=normalize(GetCameraPositionWS()-positionWS); float3 halfDir=SafeNormalize(lightDir+viewDir); float NdotH=saturate(dot(normalWS,halfDir)); float specularTerm=pow(NdotH,_Smoothness*255); half3 resultColor= baseColor.rgb*_BaseColor.rgb*lightColor*NdotL +specularTerm*lightColor.rgb*_SpecularColor.rgb; return half4(resultColor.rgb,baseColor.a); } ENDHLSL } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。