Unity的URP下法线计算

回到目录

大家好,我是阿赵。
之前写过发现贴图的计算方法,可以回顾一下:
法线贴图的计算方式

这里写一个HLSL的版本,再顺便说一下一些差异的地方

一、完整shader

Shader "azhao/NormalHLSL"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_NormalTex("Normal Tex", 2D) = "black"{}
		_normalScale("normalScale", Range(-1 , 1)) = 0
		_specColor("SpecColor",Color) = (1,1,1,1)
		_shininess("shininess", Range(1 , 100)) = 1
		_specIntensity("specIntensity",Range(0,1)) = 1
		_ambientIntensity("ambientIntensity",Range(0,1)) = 1
	}
		SubShader
		{
			Tags { "RenderType" = "Opaque" }
			LOD 100

			Pass
			{
				cull off
				HLSLPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl"
				#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
				struct appdata
				{
					float4 vertex : POSITION;
					float2 uv : TEXCOORD0;
					float3 normal:NORMAL;
					float4 tangent:TANGENT;
				};

				struct v2f
				{
					float4 pos : SV_POSITION;
					float2 uv : TEXCOORD0;
					float3 worldPos : TEXCOORD1;
					//为了构建TBN矩阵,所以要获取下面这三个值
					float3 worldNormal : TEXCOORD2;
					float3 worldTangent :TEXCOORD3;
					float3 worldBitangent : TEXCOORD4;
				};
				CBUFFER_START(UnityPerMaterial)
				float4 _MainTex_ST;
				float4 _NormalTex_ST;
				float _normalScale;
				float4 _specColor;
				float _shininess;
				float _specIntensity;
				float _ambientIntensity;
				CBUFFER_END
				TEXTURE2D(_MainTex);
				SAMPLER(sampler_MainTex);
				TEXTURE2D(_NormalTex);
				SAMPLER(sampler_NormalTex);

				//获取HalfLambert漫反射值
				float GetHalfLambertDiffuse(float3 worldNormal)
				{
					Light light = GetMainLight();
					float3 lightDir = normalize(light.direction);
					float NDotL = saturate(dot(worldNormal, lightDir));
					float halfVal = NDotL * 0.5 + 0.5;
					return halfVal;
				}

				//获取BlinnPhong高光
				float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
				{
					float3 viewDir = normalize(GetCameraPositionWS().xyz - worldPos);
					Light light = GetMainLight();
					float3 lightDir = normalize(light.direction);
					float3 halfDir = normalize((viewDir + lightDir));
					float specDir = max(dot(normalize(worldNormal), halfDir), 0);
					float specVal = pow(specDir, _shininess);
					return specVal;
				}

				v2f vert(appdata v)
				{
					v2f o;
					VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
					o.pos = vertexInput.positionCS;
					o.uv = TRANSFORM_TEX(v.uv, _MainTex);
					o.worldPos = vertexInput.positionWS;
					VertexNormalInputs normalInput = GetVertexNormalInputs(v.normal, v.tangent);
					o.worldNormal = normalInput.normalWS;
					o.worldTangent = normalInput.tangentWS;
					o.worldBitangent = normalInput.bitangentWS;

					return o;
				}

				half4 frag(v2f i) : SV_Target
				{
					//采样漫反射贴图的颜色
					half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, i.uv);
					//计算法线贴图的UV
					half2 normalUV = i.uv * _NormalTex_ST.xy + _NormalTex_ST.zw;
					//得到切线空间的法线方向
					half3 normalVal = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalTex, sampler_NormalTex, normalUV), _normalScale); 
					//构建TBN矩阵
					float3 tanToWorld0 = float3(i.worldTangent.x, i.worldBitangent.x, i.worldNormal.x);
					float3 tanToWorld1 = float3(i.worldTangent.y, i.worldBitangent.y, i.worldNormal.y);
					float3 tanToWorld2 = float3(i.worldTangent.z, i.worldBitangent.z, i.worldNormal.z);					
					//通过切线空间的法线方向和TBN矩阵,得出法线贴图代表的物体世界空间的法线方向
					float3 worldNormal = float3(dot(tanToWorld0, normalVal), dot(tanToWorld1, normalVal), dot(tanToWorld2, normalVal));

					//如果使用TransformTangentToWorld方法,上面构建矩阵求世界空间法线方向的过程可以简略成这样写
					//half3 worldNormal = TransformTangentToWorld(normalVal, half3x3(i.worldTangent.xyz, i.worldBitangent, i.worldNormal));

					//用法线贴图的世界空间法线,算漫反射
					half diffuseVal = GetHalfLambertDiffuse(worldNormal);					
					
					//用法线贴图的世界空间法线,算高光角度
					half specVal = GetBlinnPhongSpec(i.worldPos, worldNormal);
					half3 specCol = _specColor.rgb * specVal *_specIntensity;

					//最终颜色 = 环境色+漫反射颜色+高光颜色
					half3 finalCol = UNITY_LIGHTMODEL_AMBIENT.rgb * _ambientIntensity + col.rgb*diffuseVal + specCol;
					return half4(finalCol,1);
				}
				ENDHLSL
			}
		}
}

二、需要注意的地方

1、导入core.hlsl和Lighting.hlsl,因为需要用到空间转换方法和获取光照方向
2、VertexNormalInputs结构体和GetVertexNormalInputs方法。
和顶点的转换使用的GetVertexPositionInputs方法类似,core里面也提供了转换法线的方法GetVertexNormalInputs,它会返回一个结构体,里面包含了世界空间的法线、切线和bitangent。
我这里刚好三种都用到了,所以直接用这个方法,就不需要自己逐个算。如果不需要全部数据,也可以单独算其中一种,省点计算量

struct VertexNormalInputs
{
    real3 tangentWS;
    real3 bitangentWS;
    float3 normalWS;
};
VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS)
{
    VertexNormalInputs tbn;

    // mikkts space compliant. only normalize when extracting normal at frag.
    real sign = tangentOS.w * GetOddNegativeScale();
    tbn.normalWS = TransformObjectToWorldNormal(normalOS);
    tbn.tangentWS = TransformObjectToWorldDir(tangentOS.xyz);
    tbn.bitangentWS = cross(tbn.normalWS, tbn.tangentWS) * sign;
    return tbn;
}

3、UnpackNormalScale方法
UnpackNormalScale是在Packing.hlsl里面提供的方法,由于core.hlsl里面已经引用了packing,所以我们可以直接使用。

real3 UnpackNormalScale(real4 packedNormal, real bumpScale)
{
#if defined(UNITY_NO_DXT5nm)
    return UnpackNormalRGB(packedNormal, bumpScale);
#else
    return UnpackNormalmapRGorAG(packedNormal, bumpScale);
#endif
}
// Unpack from normal map
real3 UnpackNormalRGB(real4 packedNormal, real scale = 1.0)
{
    real3 normal;
    normal.xyz = packedNormal.rgb * 2.0 - 1.0;
    normal.xy *= scale;
    return normal;
}
// Unpack normal as DXT5nm (1, y, 0, x) or BC5 (x, y, 0, 1)
real3 UnpackNormalmapRGorAG(real4 packedNormal, real scale = 1.0)
{
    // Convert to (?, y, 0, x)
    packedNormal.a *= packedNormal.r;
    return UnpackNormalAG(packedNormal, scale);
}

4、关于TBN矩阵
之前为了说明白原理,我都是不厌其烦的组建一个TBN矩阵来运算,其实如果用TransformTangentToWorld方法来转换,可以把转换TBN矩阵的过程简化成

half3 worldNormal = TransformTangentToWorld(normalVal, half3x3(i.worldTangent.xyz, i.worldBitangent, i.worldNormal));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
内置的渲染器,HD,URP和轻量级SRP支持 NEW!HDRP矢量位移样本 新!URP和HDRP的镶嵌选项 新!URP的半透明和透射选项 新!新的“开始屏幕”窗口 NEW!轻松的图形共享和画布截图按钮 新功能!SRP包自动导入程序 NEW!与Unity 2019的兼容性 新!支持后期处理堆栈着色器 新功能!与Unity插件中的Substance 兼容 !支持自定义渲染纹理 新增!同时支持高清,URP和轻量级SRP 。多遍模板 !Xbox One / PS4 / Switch支持 新增!地形支持 NEW! 着色器模板 •通用PBR /未照明SRP •通用2D点亮/未照明SRP •HD点亮/未照明/头发/织物/贴花SRP •轻量PBR /未照明SRP •自定义RT初始化/更新 •后处理效果,包括后处理堆栈 • Alpha混合颗粒 •雪碧 •熄灭 •不亮光照贴图 •UI 新工具 •后处理堆栈工具 新样本 •HDRP向量位移 •独立于比例的图块 • Raphael Ernaelsten的体积像素化 •SRP HD全贴图 •马赛克效果 •未使用光照贴图 新模板 •通用PBR /未照明 •HD点亮/ 未照明/毛发/织物 •后处理堆栈 •未照明光图 新节点 •反投影矩阵 •反视图投影矩阵 •HD发射 •Voronoi •渐变 •渐变样本 新增的着色器功能 •反勒普 •随机范围 •SRP附加光 •流量 •旋转 •高处法线 •噪声正弦波 •锯齿波 •方波 •三角波 •棋盘格 •椭圆 •多边形 •矩形 •圆角矩形 最新改进 •添加了专门用于新通用渲染管线的 模板•添加了与Unity HDRP着色器检查器兼容的新HD Lit模板 •无限循环检测现在更快,减少了连接大型图形上的节点时的命中率。 •改进了节点预览渲染刷新行为 •创建了新的标记系统以改善节点搜索 •只需单击“屏幕截图”按钮即可获取整个画布的屏幕截图 •通过“共享”按钮轻松共享图形的选定部分 •添加了新的后处理堆栈工具它会使用给定着色器的PPS渲染器和设置生成cs脚本。 •Amplify Shader Editor通过高达v7.2.x的模板支持HD,Lightweight和Universal RP。 •Legacy HD和Lightweight SRP v3.xx / v4.xx / v5.xx模板也通过Legacy软件包提供。 •通过自定义RT模板支持在Unity 2017及更高版本上使用自定义渲染纹理。 •现在,也可以通过Unity插件中的Substance,在Unity 2018及更高版本的ASE画布上使用此Substance。 •现在在Unity 2018.2及更高版本上可以访问8个UV通道。 •可以通过键盘方向键平移和缩放ASE画布摄像机。 •支持HD PBR SRP模板中的材料类型。 •支持将Specular工作流程集成到Lightweight PBR SRP模板中。 •现在可以直接在模板上添加自定义选项。 了解更多: 在此处放大Wiki 讨论:Unity论坛线程 着色器示例:完整列表 编辑器在将来的更新中将继续得到改进,当前正在开发许多功能。 显着功能 •完整的源代码 •支持Xbox One / PS4 / Switch • 自定义节点API • 着色器模板 • 着色器功能 •多窗口支持 •直观,熟悉的节点界面 •广泛的节点库 •实例化支持 •用户贡献的节点和着色器 •不断增加的样本收集

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值