Shader学习之路——凹凸映射

方法一:使用一张高度纹理来模拟表面位移,然后得到一个修改后的法线值,这种方法也叫做高度映射

方法二:使用一张法线纹理来直接存储表面法线,这种方法叫做法线映射

高度纹理:高度图中存储的是强度值,用于表示模型表面局部的海拔高度,颜色越浅表示越向外凸起,而颜色越深表明该位置越向里凹

优点:更加直观的知道一个模型表面的凹凸情况

缺点:计算更复杂,不能直接得到表面法线,而是要由像素的灰度值计算而得

法线纹理:法线纹理中存储的是法线方向。由于法线方向的分量范围是在[-1,1],而像素的分量为[0,1],因此我们需要做一个映射,通常:

                        pixel = (normal + 1) / 2

这就要求我们在对法线纹理采样之后进行一次反映射得到原来的法线方向 normal = pixed * 2 - 1

对于模型每个顶点都有切线空间,这个切线空间的原点就是该顶点本身,而Z轴是顶点的法线方向(n),x轴是顶点的切线方向(t),而Y轴可由法线和切线的叉积而得,也称副切线或者副法线

模型空间来存储法线的优点:

        实现简单,更加直观,计算少,生成也非常简单。而如果要生成切线空间下法线纹理,由于模型的切线一般是和UV方向相同,因此想要得到效果比较好的法线映射就要求纹理映射也是连续的。

切线空间来存储法线的优点:

        自由度很高。模型空间下法线纹理记录的是绝对法线信息,仅可以用于创建它时的那个模型,而应用到其他模型上效果就完全错误了。而切线空间下的法线纹理记录的是相对法线信息,这意味着,即便吧该纹理应用到一个完全不同的网格上,也可以得到一个合理的结果

        可以进行UV动画,比如可以移动纹理的UV坐标来实现一个凹凸移动的效果,这种UV动画在水或者火山熔岩这种类型的物体上会经常用到。

        可以重用法线纹理

        可以压缩。由于切线空间下的法线纹理中法线的Z方向总是正的所以我们仅存XY方向即可

 

 

Shader "Unlit/NormalMapTangentSpace"
{
	Properties
	{
		_MainTex("Texture",2D) = "white" {}
		_BumpMap("Normal Map",2D) = "bump" {}  //对应了模型自带的法线信息
		_BumpScale("Bump Scale",Float) = 1.0 //控制凹凸程度
		_Specular("Specular",Color) = (1,1,1,1)
		_Gloss("Gloss",Range(8.0,256)) = 20
	}
		SubShader
		{
			Tags { "LightMode" = "ForwardBase" }
			LOD 100

			Pass
			{
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "Lighting.cginc"
				#include "UnityCG.cginc"

				struct appdata
				{
					float4 vertex : POSITION;
					float3 normal:NORMAL;
					float4 texcoord:TEXCOORD0;
					float4 tangent:TANGENT;
				};

				struct v2f
				{
					float4 uv : TEXCOORD0;
					float4 vertex : SV_POSITION;
					float3 lightDir:TEXCOORD1;
					float3 viewDir:TEXCOORD2;
				};

				sampler2D _MainTex;
				sampler2D _BumpMap;
				float _BumpScale;
				fixed4 _Specular;
				float _Gloss;
				float4 _MainTex_ST;
				float4 _BumpMap_ST;

				v2f vert(appdata v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
					o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
					//副法线方向 = corss(法线,切线)   x:切线方向   y:顶点法线方向  z:副法线
					//与W相乘是因为与xy垂直的方向有两个  w决定哪个方向
					float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
					//从模型空间转换到切线空间的矩阵
					float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);//TANGENT_SPACE_ROTATION

					//灯光方向转到切线空间
					o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
					o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
					fixed3 tangentLightDir = normalize(i.lightDir);
					fixed3 tangentViewDir = normalize(i.viewDir);
					//根据法线贴图得到法线
					fixed4 packedNormal = tex2D(_BumpMap,i.uv.zw);
					fixed3 tangentNormal;
					//根据法线贴图反映射得到原切线数据 像素[0,1]  法线[-1,1]
					tangentNormal.xy = UnpackNormal(packedNormal); //(packedNormal.xy * 2 - 1);					
					tangentNormal.xy *= _BumpScale;
					//保证法线方向的z分量为正
					tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

					fixed3 albedo = tex2D(_MainTex, i.uv);

					fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));

					fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

					//反射方向
					fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
					fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);

					return fixed4(ambient + diffuse + specular,1);
				}
				ENDCG
			}
		}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Shader(着色器)是用于在图形渲染过程中处理和操作像素数据的一种程序。学习Shader需要一定的编程基础,特别是对C++、OpenGL或DirectX等图形编程框架的熟悉。以下是一个关于学习Shader的计划: 1. 学习基本概念: - 了解计算机图形学的基本概念,如顶点、纹理、光照等。 - 学习Shader的基本组成部分,包括顶点着色器(Vertex Shader)、片段着色器(Fragment Shader)和几何着色器(Geometry Shader)。 2. 选择合适的编程语言和图形编程框架: - 根据个人喜好和项目需求,选择C++、C#或HLSL(High Level Shading Language)作为编写Shader的编程语言。 - 学习所选编程语言的基本语法和图形编程框架的使用方法。 3. 学习Shader语言: - 熟悉所选编程语言的Shader库,了解常用的Shader函数和结构。 - 学习编写基本的顶点着色器和片段着色器,掌握如何描述图形渲染的过程。 4. 学习高级Shader技术: - 学习纹理贴图、光照模型、阴影技术等高级Shader技术。 - 掌握如何使用多个着色器(如几何着色器和光栅化着色器)组合在一起实现复杂的渲染效果。 5. 实践项目: - 参与开源项目或自己创建项目,将所学的Shader知识应用到实际项目中。 - 通过不断地实践和优化,提高自己的Shader编程能力。 6. 深入学习: - 阅读相关书籍和教程,了解更多关于Shader的高级技术和实现方法。 - 参加线上或线下的技术社区,与其他开发者交流学习经验,共同进步。 总之,学习Shader需要时间和耐心,通过不断地学习和实践,你将能够掌握这一技能并在图形渲染领域取得突破。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值