法线纹理采样+可视化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]为例)

实现效果如图:

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

剩余属性可自行探索。

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

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

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

相关推荐
就叫啥也不会吧7 小时前
VSCode突然连接不上服务器(已解决)
ide·vscode·编辑器
larito14 小时前
Unity尸潮高性能技术方案(寻路+碰撞+动画)
unity·游戏引擎
15 小时前
Unity 与 Lua 交互详解
unity·lua·交互
向宇it20 小时前
【unity游戏开发入门到精通——UGUI】UGUI自动布局组件
游戏·ui·unity·c#·游戏引擎
barbyQAQ1 天前
解决vscode cmake提示检测到 #include 错误
ide·vscode·编辑器
kooboo china.1 天前
Tailwind CSS实战技巧:从核心类到高效开发
前端·javascript·css·编辑器·html
谢斯1 天前
[Unity]设置自动打包脚本
unity·游戏引擎
EQ-雪梨蛋花汤1 天前
【Unity AR开发插件】一、高效热更新:Unity AR 插件结合 HybridCLR 与 ARFoundation 的开源仓库分享
unity·开源·ar
没事写写笔记1 天前
Unity SpriteAtlas (精灵图集)
unity