法线纹理采样+可视化Shader编辑器

法线贴图,对主纹理凹凸显示

建模原理

法线贴图:切线空间,存储xy切线,映射法线,法线信息存储在切线空间中。

模型是否凹凸,是由模型顶点决定的,现在实现的法线贴图,控制凹凸,实际上是配合

光照实现的,凹进去的部分,颜色偏暗,突出来的部分,颜色偏亮。

导入法线贴图

贴图类型转换为Normal Map,法线纹理类型。

法线贴图计算

法线贴图:存储有与法线垂直的切线信息,切线信息存储在切线空间中,使用内部

值(x切线,y切线)时,需要将法线转换到世界空间中,再进行光照运算才能得到

正确的结果。调整凹凸深度的参数:用户可配置,可以控制法线长短。

Shader实现

加载两张纹理:主纹理,光照法线纹理(切线空间存储数据)

顶点着色器:

主纹理的UV偏移计算

法线纹理的UV偏移计算

计算切线空间到世界空间的转换矩阵(可变),用于变换光照法线

片元着色器

解压主纹理

解压法线纹理,根据切线信息,转换光照法线信息,将光照法线从切线空间,

转到世界空间

拿法线纹理算出的光照法线,做光照运算。

相关实现代码示例如下所示:

cs 复制代码
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "CreateTest/PhongNormalTexture"
{
    Properties
    {
        //用于显示材质纹理
        _MainTex("主纹理",2D)="white"{}
        //用于和主纹理混色
        _Color("混色",Color)=(1,1,1,1)
        //法线纹理
        _BumpTex("法线纹理",2D)="bump"{}
        //法线深度系数,可以控制法线高度
        _BumpScale("法线深度系数",Float) = 1
        _SpecularColor("高光反射材质颜色",Color) = (1,1,1,1)
        _Gloss("光晕系数",Range(8,256)) = 10
    }
        SubShader
        {
            Tags{"LightMode" = "ForwardBase"}
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "Lighting.cginc"
                //导入主纹理数据
                sampler2D _MainTex;
                float4 _MainTex_ST;
                fixed4 _Color;
                //导入法线信息
                sampler2D _BumpTex;
                float4 _BumpTex_ST;
                float _BumpScale;

                //导入高光信息
                fixed4 _SpecularColor;
                float _Gloss;

                //从CPU过来的数据
                struct c2v
                {
                    float4 vertex:POSITION;//从CPU传递过来的模型空间下,需要渲染的点
                    float4 texcoord : TEXCOORD0; //因为两张贴图的像素,除颜色外完全重叠,所以纹理坐标点可以通用
                    float4 tangent:TANGENT;  //光照法线纹理,因为要计算切线空间下的信息,所以需要渲染点的切线信息
                    float3 normal:NORMAL;
                };
                struct v2f
                {
                    float4 pos:SV_POSITION;  //模型空间到裁剪空间转换后的点
                    float4 uv:TEXCOORD1;  //因为要算出两张纹理的uv坐标,所以做一个float4,xy存储主纹理UV,zw存储法线纹理UV

                    float4 MatrixRowOne:TEXCOORD2;  //用于传递从顶点着色器计算好的转换矩阵
                    float4 MatrixRowTwo:TEXCOORD3;
                    float4 MatrixRowThree:TEXCOORD4;
                };
                v2f vert(c2v data)
                {
                    v2f r;
                    r.pos = UnityObjectToClipPos(data.vertex);
                    //两张纹理的缩放和偏移可能不同,所以分别计算uv偏移信息,存储在v2f.uv
                    r.uv.xy = data.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                    r.uv.zw = data.texcoord.xy * _BumpTex_ST.xy + _BumpTex_ST.zw;
                    
                    //CPU传递过来的切线存储在模型空间下
                    //法线纹理中的,光照法线推算信息,是存储在切线空间下的
                    //CPU传递过来的切线信息与法线纹理中的切线信息,有转换关系,所以可以推算出一个转换矩阵用于切换法线
                    //计算出来的转换矩阵应该是从(切线空间到模型空间)的转换
                    //但是我们需要的是计算(从切线空间到世界空间)的转换矩阵(因为最终的光照运算,需要在世界空间中完成)
                    //所以应该先把CPU传递过来的切线信息,转换到世界空间下
                    //再计算切线的转换矩阵,这时就能得到从(切线空间,到世界空间)的转换矩阵
                    //拥有了转换矩阵,就可以将法线纹理中,求解的法线信息,从切线空间,转换到世界空间下,进而可以计算光照
                
                    //世界坐标系下的点的位置(片元着色器计算光照需求)
                    float4 worldPos = mul(unity_ObjectToWorld, data.vertex);
                    //世界空间下渲染点的法线信息
                    float3 worldNormal = mul((float3x3)unity_ObjectToWorld, data.normal);
                    //世界空间下切线的方向向量
                    float3 worldTangent = mul((float3x3)unity_ObjectToWorld, data.tangent.xyz);

                    //世界空间下计算与切线和法线垂直的线的方向向量(用于计算转换矩阵)
                    float3 worldBinormal=cross(worldNormal, worldTangent)* data.tangent.w;

                    //需要将转换矩阵,传递给片元着色器,用于转换切线空间下的法线到世界空间中
                    r.MatrixRowOne = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                    r.MatrixRowTwo = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                    r.MatrixRowThree = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
                    return r;
                }
                fixed4 frag(v2f data) :SV_Target
                {
                    //世界空间下的点
                    float3 worldPos = float3(data.MatrixRowOne.w,data.MatrixRowTwo.w,data.MatrixRowThree.w);
                    //计算法线纹理中法线信息(重点),解出的法线在切线空间
                    //解法线前,需要先对法线纹理贴图进行采样
                    fixed3 bump = UnpackNormal(tex2D(_BumpTex, data.uv.zw));
                    //通过缩放值,影响凹凸感
                    bump.xy *= _BumpScale;
                    //计算法线高度(数学公式)
                    //法线还没有转换空间,所以计算出的法线,还在切线空间下
                    bump.z = sqrt(1 - max(0, dot(bump.xy, bump.xy)));

                    //通过顶点着色器传递过来的转换矩阵,转换法线,从切线空间到世界空间
                    bump = float3(dot(data.MatrixRowOne.xyz, bump), dot(data.MatrixRowTwo.xyz, bump), dot(data.MatrixRowThree.xyz, bump));
                    //解主纹理
                    fixed4 texColor = tex2D(_MainTex, data.uv.xy) * _Color;
                    //计算漫发射光照
                    fixed3 diffuse = _LightColor0.rgb * texColor.rgb * max(0, dot(normalize(bump), normalize(_WorldSpaceLightPos0.xyz)));
                    //计算高光反射光照
                    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - worldPos);
                    fixed3 refDir = normalize(reflect(-_WorldSpaceLightPos0.xyz, normalize(bump)));
                    fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, refDir)), _Gloss);
                    //Phong光照运算
                    fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz * texColor.rgb + diffuse + specular;
                    return fixed4(color, 1);
                }
            ENDCG
            
        }
    }
    Fallback "Diffuse"
}

其实现效果如下图:

左侧为Standard的shader效果,右侧为上面代码下的shader效果,右侧较左边多了高光反射的相应数据,考虑到砖墙等一些粗糙表面现实情况下不会有如此明显的高光,应用此shader文件的同学可以将Phong光照模型的公式中的高光反射部分去除,在其他需要高光反射的情况下再进行视情况添加即可,法线贴图下第一个属性值为法线深度系数(数值0.6处,由于Shader文件未设置成UTF-8导致的中文乱码,读者可自行设置Shader文件的格式或采用英文命名),可通过调节系数对应加强法线纹理效果。

以下内容作者提供一个网址,方便读者下载Amplify Shader Editor,可找到该插件的网址如下:

amplify_shader_pack unity3D_游戏3d模型 免费下载 - 爱给网

插件中提供了很多较高质量动态Shader,有需要的可自行下载。

例:

导入Unity包后即可使用该可视化Shader编辑器,使用的Unity包例:

使用编辑器的过程:

若两边窗口未展开,点击左右上角的银灰色方框即可展开,部分使用示例如下:

更改Shader名称:

添加并编辑某一属性:(以Texture Sample T为例)

实现效果如图:

左侧黄色按钮,黄色为未保存,单击使其变为绿色,则成功保存期间的设置。

剩余属性可自行探索。

实现上面代码的功能对应在可视化编辑器中的操作结果如下所示:

其在检查器窗口的情况如下:

该系列专栏为网课课程笔记,仅用于学习参考。

相关推荐
心前阳光17 小时前
Unity发布PC软件图标不能改变
unity·游戏引擎
an86950011 天前
unity如何在visual studio中打断点debug
unity·游戏引擎·visual studio
xcLeigh1 天前
Unity基础:使用Transform控制物体移动——Translate与position详解
unity·游戏引擎
FairGuard手游加固1 天前
Unity小游戏加密:global-metadata.dat与AssetBundle保护
游戏·unity·游戏引擎
真鬼1231 天前
【Unity AI】Untiy链接cursor与MCP
unity·游戏引擎
WarPigs1 天前
Unity设置人物位置无效的原因
unity
程序员正茂1 天前
Unity3d行为树Behavior Designer Pro
unity·行为树
爱上网的小舟1 天前
着色器缓存大小设置多少合适?N卡A卡缓存路径与清理方法
缓存·着色器
玖玥拾1 天前
Lua 基础语法(八) Unity Huatuo (HybridCLR)
开发语言·unity·游戏引擎·lua
平行云1 天前
国产GPU云渲染适配实战:驱动兼容、编码调优与多路并发
unity·ue5·webrtc·webgl·实时云渲染·云桌面·像素流送