【Unity Shader】永远的主视图

这篇博客探讨了如何使用Unity的Shader实现无论视角如何变化,模型展示的效果始终保持一致。通过将模型上的坐标点转化为相对于选定中心点的新坐标系,确保在不同角度观察时,视觉体验恒定。
Unity中,X、Y、Z坐标轴分别对应左、上、前,摄像机人视觉的方向在Z轴上。所以,当我们从Z轴看物体看的是正反面,Y轴看的是上下面,X看的是左右面。
一个面片Quad只有四个顶点、两个三角形,我们从Z上看是矩形,但是从其他的两个方向上看我们是看不见的。而场景中烟花的粒子,一个正在旋转的黑洞等大都是一个面片,我们希望从任意角度看这个物体时它都和从正面看到的一样,怎么办呢?简单的想想有两种方法:
1,让面片永远的LookAt 摄像机(个人觉得有点Low)
2,使用Shader重绘顶点
3,没想...
现在说说关于2的实现。
如下面第一张图是我们正常的视觉,把物体的中心点设置为坐标的元点,从正前方看。当我们改变视线,从Z1向物体看去,把原来的坐标轴按照新的视线进行对应得到图2。现在我们的目的转换成求转换后坐标轴即可,把点转换成我们新构建的坐标空间。


得到Z1是新的视觉方向,那么先预计一个大概的Y方向。如果新的Z1靠近原Y,那么我们估计他的新Y为(0,0,1),否则还当作(0,1,0), 只要这两个向量是在同一个水平面上就行。然后通过向量的叉乘求得X1:
X1 = 预估Y1 x Z1;
再去求得准确的Y1:
Y1 = Z1 x X1;

得到改变后的坐标向量(X1,Y1,Z1),把模型上的坐标点距离我们选定的中心点的向量转换到新的坐标系中就可。

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Custom/MainView" {
	Properties {
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
	}

	SubShader{
		Tags{"Queue"="Transparent" "RenderType"="Transparent" "IgnoreProject"="True" "DisableBatching"="True"}
		Pass{
			Tags{"LightMode"="ForwardBase"}

			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha
			Cull Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			fixed4 _Color;
			sampler2D _MainTex;

			struct a2v {
				float4 vertex : POSITION;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			v2f vert(a2v v) {
				v2f o;
				float3 center = float3(0,0,0);
				float3 viewerPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));

				float3 z1 = normalize(viewerPos - center);//视觉向量,新的Z1
				//上方,模型的向上方向向量,和视觉方向垂直,当视觉方向接近垂直时设为世界坐标的水平方向
				float3 y1 = abs(z1.y) > 0.999 ? float3(0,0,1) : float3(0,1,0);
				float3 x1 = normalize(cross(y1, z1));//叉乘得垂直
				y1 = normalize(cross(z1, x1));//获取准确的向上分量

				float3 curVector = v.vertex.xyz - center;//当前坐标向量
				//向量在三个方向上的分量为最终坐标,相当于重构坐标轴
				float3 newPos = center + x1 * curVector.x + y1 * curVector.y + z1 * curVector.z;//转化到新坐标系
				o.pos = mul(UNITY_MATRIX_MVP, float4(newPos, 1));
				o.uv = v.texcoord.xy;
				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				return tex2D(_MainTex, i.uv) * _Color;
			}

			ENDCG
		}
	}

	FallBack Off
}

DisableBatching的作用说过了,其他的都是满满的线性代数0.0


可以看到当我们旋转从不同角度看时看到的基本上都是一样的。

Unity 中实现凹凸纹理效果(Bump Mapping)通常通过法线贴图(Normal Mapping)来完成。法线贴图是一种逐像素修改法线方向的技术,它可以让低多边形模型在光照下呈现出高多边形模型的细节效果。以下是关于如何在 Unity Shader 中实现这一效果的详细说明。 ### 法线贴图原理 在传统的光照计算中,法线是从模型顶点数据中获取的,它决定了表的朝向和光照响应。然而,对于低数模型来说,这种法线信息无法表现出复杂的表细节。法线贴图通过将每个像素的法线方向存储在一张纹理中,从而在不增加模型复杂度的前提下模拟出凹凸不平的表效果。 在像素着色器中,使用法线贴图时,需要将法线信息从纹理中采样出来,并将其转换到世界空间或视图空间,以参与光照计算。这种方法可以让表在不同光照角度下呈现出更加丰富的细节[^4]。 ### Unity Shader 实现步骤 #### 1. 准备法线贴图 在 Unity 中,首先需要一张法线贴图(Normal Map)。这张贴图通常是以切线空间(Tangent Space)的形式存储的。在导入贴图时,需要将其设置为“Normal Map”类型,Unity 会自动将其转换为适合着色器使用的格式。 #### 2. 使用 `UnpackNormal` 函数 由于法线贴图在纹理中是以压缩格式存储的(通常是 [0, 1] 范围内的 RGB 值),因此在着色器中需要使用 `UnpackNormal` 函数将其还原为 [-1, 1] 范围的法线向量: ```hlsl fixed4 normalTex = tex2D(_BumpTex, i.uv); float3 normal = UnpackNormal(normalTex); ``` 该函数会将纹理中的颜色值转换为正确的法线向量,并考虑 `_BumpScale` 参数来控制凹凸程度[^2]。 #### 3. 构建 TBN 矩阵 为了将法线从切线空间转换到世界空间,需要构建 TBN(Tangent-Binormal-Normal)矩阵。在 Unity 中,顶点结构通常包含 `tangent` 和 `normal` 成员,可以通过它们计算出副法线(Binormal)并构造出 TBN 矩阵: ```hlsl float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w; float3x3 TBN = float3x3(v.tangent.xyz, binormal, v.normal); ``` 这样构造出的矩阵可以将法线从切线空间变换到世界空间: ```hlsl float3 worldNormal = normalize(mul(TBN, normal)); ``` #### 4. 应用光照计算 最后,使用变换后的法线向量进行光照计算,例如 Phong 或 Blinn-Phong 模型: ```hlsl float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float diffuse = max(0, dot(worldNormal, lightDir)); ``` ### 完整示例代码 以下是一个简化的 Unity Shader 示例,展示了如何实现法线贴图效果: ```hlsl Shader "Custom/NormalMapShader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _BumpTex ("Normal Map", 2D) = "bump" {} _BumpScale ("Bump Scale", Float) = 1.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; sampler2D _BumpTex; float _BumpScale; struct Input { float2 uv_MainTex; float2 uv_BumpTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; fixed4 bump = tex2D(_BumpTex, IN.uv_BumpTex); o.Normal = UnpackNormal(bump); o.Normal.z /= _BumpScale; } ENDCG } FallBack "Diffuse" } ``` ### 优化与扩展 - **Mipmap 与 Detail Map**:在处理法线贴图时,也可以结合 Mipmap 技术来优化远处的渲染效果。通过设置合适的 Mipmap 层级和使用三线性过滤,可以避免法线贴图在远处出现模糊或闪烁现象[^3]。 - **Detail Map 混合**:可以将细节贴图(Detail Map)叠加到主贴图上,以增强近距离观察时的表细节。这通常通过简单的颜色叠加或乘法操作实现。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值