钢铁侠材质制作——4、反射效果叠加

news/2024/5/19 6:59:09 标签: 材质, unity, 游戏引擎, 反射, MatCap

钢铁侠Unlit光照Shader,三种效果变化

返回目录

大家好,我是阿赵。这里是钢铁侠材质制作的第四部分,反射效果叠加。

1、光照模型的补充说明

我一直说光照模型的构成是
环境光+漫反射+高光。
其实最后一个高光,应该理解成是光线的反射
所以最后光照模型的构成就变成了
环境光+漫反射+光线反射颜色

在之前的基础光照模型的漫反射和高光颜色之后,这一步需要加入反射的效果。

2、反射效果介绍

光线反射的实现有很多种,比如像Unity引擎官方例子的水面反射,或者是用CubeMap、反射探针(reflection probe)等都是可以的。
由于这个综合的例子本身已经有点复杂,出于性能考虑,这里选用了MatCap作为模拟反射的手段。为了能让模型的边缘有一些类似于补光的效果,所以我还加入了一点边缘光。

怎样使用MatCap材质之前已经介绍过,这里也不再多说,先看看效果,然后直接上代码。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里主要使用了2张贴图,一张是金属感比较强的MatCap材质,另外一张是之前用的高光遮罩贴图的G通道。

在这里插入图片描述

可以看到,加了MatCap模拟反射之后,模型的细节会变得更加的丰富。

3、完整Shader:

Shader "azhao/IronManBodyCode"
{
    Properties
    {
		_RimBias("RimBias", Float) = 1
		_RimPow("RimlPow", Float) = 2
		_RimlCol("RimCol", Color) = (0,0,0,0)
		_NoiseMap("NoiseMap",2D) = "black"{}
		_NoiseTiling("NoiseTiling",Vector) = (1,1,0,0)
		_NoiseSpeed("NoiseSpeed",float) = 0

		_AmbientStength("AmbientStength",float) = 1
		_MainTex("BaseCol",2D) = "white"{}
		_NormalMap("NormalMap",2D) = "black"{}
		_NormalScale("NormalScale",float) = 1
		_SpecCol("SpecCol",Color) = (1,1,1,1)
		_Shininess("_Shininess",float) = 1
		_SpecStength("SpecStength",float) = 1
		_SpecMask("SpecMask",2D) = "white"{}

		_MatCapTex("_MatCapTex",2D) = "black"{}
		_MatCapStength("MatCapStength",float) = 1
		_MatCapPow("MatCapPow",float) = 2
		_RimAddStength("RimAddStength",float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


            #include "UnityCG.cginc"

			//简化版的转换法线并缩放的方法
			half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
			{
				half3 normal;
				//由于法线贴图代表的颜色是0到1,而法线向量的范围是-1到1
				//所以通过*2-1,把色值范围转换到-1到1
				normal = packednormal * 2 - 1;
				//对法线进行缩放
				normal.xy *= bumpScale;
				//向量标准化
				normal = normalize(normal);
				return normal;
			}


			//获取HalfLambert漫反射值
			float GetHalfLambertDiffuse(float3 worldPos, float3 worldNormal)
			{
				float3 lightDir = UnityWorldSpaceLightDir(worldPos);
				float NDotL = saturate(dot(worldNormal, lightDir));
				float halfVal = NDotL * 0.5 + 0.5;
				return halfVal;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal,float shininess)
			{
				float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
				float specDir = max(dot(normalize(worldNormal), halfDir),0);
				float specVal = pow(specDir, shininess);
				return specVal;
			}

			float2 GetMatCapUV(float3 normalWorld)
			{
				float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
				return normalView.xy*0.5 + 0.5;
			}



            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;
				float3 worldNormal : TEXCOORD2;
				float3 worldTangent :TEXCOORD3;
				float3 worldBitangent : TEXCOORD4;
				
            };

			float _RimBias;
			float _RimPow;
			float4 _RimlCol;
			sampler2D _NoiseMap;
			float4 _NoiseTiling;
			float _NoiseSpeed;

			sampler2D _MainTex;
			sampler2D _NormalMap;
			float _NormalScale;
			float _AmbientStength;
			float4 _SpecCol;
			float _Shininess;
			float _SpecStength;
			sampler2D _SpecMask;

			sampler2D _MatCapTex;
			float _MatCapStength;
			float _MatCapPow;
			float _RimAddStength;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldTangent = UnityObjectToWorldDir(v.tangent);
				o.worldBitangent = cross(o.worldNormal, o.worldTangent);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {

				float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				float NdotV = dot(i.worldNormal, worldViewDir);
				float fresnelVal = pow((1 - NdotV)*_RimBias, _RimPow);
				
				/*
				光线轮廓暂时先屏蔽掉
				float2 noiseUV = i.worldPos.xy *_NoiseTiling.xy + _NoiseTiling.zw;
				noiseUV.y += frac(_Time.y)*_NoiseSpeed;
				float4 noiseCol = tex2D(_NoiseMap, noiseUV);
				half4 RimLineRGBA = _RimlCol * (fresnelVal+noiseCol.r);
				*/
				half4 RimRGBA = _RimlCol * fresnelVal*_RimAddStength;
				

				half4 baseCol = tex2D(_MainTex,i.uv);

				half4 normalCol = tex2D(_NormalMap, i.uv);
				//得到切线空间的法线方向
				half3 normalVal = UnpackScaleNormal(normalCol, _NormalScale).rgb;

				//构建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));


				

				float diffuseVal = GetHalfLambertDiffuse(i.worldPos, worldNormal);
				float4 specMaskVal = tex2D(_SpecMask, i.uv);
				float specVal = GetBlinnPhongSpec(i.worldPos, worldNormal, _Shininess)*specMaskVal.r*_SpecStength;
				float2 MatCapUV = GetMatCapUV(worldNormal);
				float4 MatCapCol = pow(tex2D(_MatCapTex, MatCapUV)*_MatCapStength*specMaskVal.g, _MatCapPow);
				half3 finalRGB = UNITY_LIGHTMODEL_AMBIENT * _AmbientStength + baseCol.rgb*diffuseVal+ _SpecCol.rgb*specVal + MatCapCol+ RimRGBA.rgb;
				return half4(finalRGB,1);
            }
            ENDCG
        }
    }
}

http://www.niftyadmin.cn/n/195225.html

相关文章

手冢国光对战幸村精市,谁才是最强初中生?

人物介绍 手冢国光 手冢国光,青学网球部部长,初三前校内赛正式比赛中保持全胜,小学时便领悟了千锤百炼,才气焕发, 初三时由于手受伤惜败与迹部真田,但是带领青学打败王者立海大,获得全国冠军。…

Python3 os.statvfs() 方法、Python3 os.lstat() 方法

Python3 os.statvfs() 方法 概述 os.statvfs() 方法用于返回包含文件描述符fd的文件的文件系统的信息。 语法 statvfs()方法语法格式如下: os.statvfs([path])参数 path -- 文件路径。 返回值 返回的结构: f_bsize: 文件系统块大小 f_frsize: 分栈大小 f_bl…

Java多线程:Thread中的实例方法

Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步。在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1、this.XXX() 这…

70-归并排序

JDK的TimeSort就使用了归并排序。 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序。若将2个有序…

OpenCV基础之模板匹配与直方图

文章目录OpenCV基础之模板匹配与直方图模板匹配直方图查看图像的三色mask掩模图像均衡直方图自适应直方图均衡化OpenCV基础之模板匹配与直方图 模板匹配 模板匹配即在一幅图像中寻找与模板图像最匹配(相似)部分的部分。简单的实例,匹配多个文件夹图案。 注意&…

让 ChatGPT 自己告诉你如何与它高效交流(对话全过程展示,请自行感受)

你好呀,我们又见面啦~ 现在我想写一个与你高效交流问答的 prompt 指南,然后发表成博客,请你给我一些建议,列个提纲,然后我再一个个问你逐个展开 您好!很高兴再次为您提供帮助。关于撰写关于与 ChatGPT 高效…

Java多线程:读写锁和两种同步方式的对比

读写锁ReentrantReadWriteLock概述 大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),但是效率非常低。所以在JDK中提供了一种读写锁Reentran…

【Docker】Docker容器数据卷

介绍 卷是目录和文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过UnionFS提供一些用于持续存储或共享数据的特性。——将docker容器内的数据保存进宿主机的磁盘中。 卷的设计目的&#xff1a…