【Unity Shaders】Lighting Models —— 衣服着色器
本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。
这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。
========================================== 分割线 ==========================================
写在前面
布料(Cloth)是另一种非常常见的着色需求,在很多实时游戏中都需要它来实现更真实的交互体验。它涉及到如何让布料的纤维合适地分散整个表面的光照,使它看起来像布料一样。布料的渲染非常依赖视角的变化,因此我们将学习一些新的技巧来模拟光扫射到布料上的效果,并且那些细小的纤维还能产生与众不同的边缘光照效果。
这篇将会介绍两种新的概念:细节法线贴图(Detail normal maps)和细节贴图(Detail textures)。通过把这两种法线贴图结合到一起,我们可以得到一种更高层次的细节表现,并且可以存储在一张2048*2048的贴图中。这种技术可以帮助我们模拟表面那种非常细微层次的凹凸不平的感觉,以此来分散整个表面的高光反射。
下面显示了本节最终得到的布料着色器效果:
准备工作
这个Shader需要结合3种不同类型的贴图来模拟布料效果:
- 一张细节法线贴图(Detail Normal map)。这张贴图将会平铺在表面上来模拟细小的缝纫痕迹。
- 一张标准变化贴图(Normal Variation map)。这张贴图将会模拟缝纫的变化,防止所有表面看起来都是一样的,而更像是有岁月磨损的样子。
- 一张细节漫反射贴图(Detail Diffuse map)。我们使用这张贴图去乘以基本颜色来模拟布料的整体颜色,以此来为整体增加更多的深度细节和真实感,并且还能强调布料的缝纫痕迹。
实现
- 首先,老样子添加新的properties。这里主要是为了控制所有的贴图和菲涅耳以及高光反射等。
Properties
{
_MainTint ("Global Tint", Color) = (1,1,1,1)
_BumpMap ("Normal Map", 2D) = "bump" {}
_DetailBump ("Detail Normal Map", 2D) = "bump" {}
_DetailTex ("Fabric Weave", 2D) = "white" {}
_FresnelColor ("Fresnel Color", Color) = (1,1,1,1)
_FresnelPower ("Fresnel Power", Range(0, 12)) = 3
_RimPower ("Rim FallOff", Range(0, 12)) = 3
_SpecIntesity ("Specular Intensiity", Range(0, 1)) = 0.2
_SpecWidth ("Specular Width", Range(0, 1)) = 0.2
}解释:菲涅耳反射,简单来讲,就是当你垂直观察平面时,反射很弱;但当视线与平面越小时,反射越明显。举个例子,当你站在水边观察水面时,水是透明的,反射很弱,但是当你离水面越远时,基本就看不到河面以下的部分了,反射很强。(百度百科)
- 由于我们想要全面控制光照对布料平面的影响,因此我们需要在#pragma语句中声明新的光照模型,并且设置使用Shader model 3.0。
CGPROGRAM
#pragma surface surf Velvet
#pragma target 3.0 - 现在,我们需要建立Properties块和SubShader块的联系。为了使用Properties中的各种数据,我们需要在SubShader中声明同样名字的变量。
sampler2D _BumpMap;
sampler2D _DetailBump;
sampler2D _DetailTex;
float4 _MainTint;
float4 _FresnelColor;
float _FresnelPower;
float _RimPower;
float _SpecIntesity;
float _SpecWidth; - 为了分别控制几种细节贴图的平铺率,我们需要在Input结构中声明它们的UV参数。如果你把uv放在相同的贴图名称的前面,就可以建立UV信息的联系。
struct Input
{
float2 uv_BumpMap;
float2 uv_DetailBump;
float2 uv_DetailTex;
}; - 现在我们需要创建我们的光照模型函数。首先需要创建光照函数结构。我们需要viewDir参数得到视角方向,这是因为布料表面是受视角影响的。
inline fixed4 LightingVelvet (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
} - 永远在一开始就处理好你所有的光照向量(这里指视角方向和光照方向向量,以及它们的衍生向量)。这样可以让你不需要总是标准化你的向量,或担心光照计算的其他部分。因此,在光照模型函数的开头添加光照向量:
//Create lighting vectors here
viewDir = normalize(viewDir);
lightDir = normalize(lightDir);
half3 halfVec = normalize (lightDir + viewDir);
fixed NdotL = max (0, dot (s.Normal, lightDir));
解释:自己画一画就知道,halfVec将lightDir和viewDir结合在一起,主要用于和这两个向量相关的计算中。例如这里的高光反射(高光反射和观察视角以及光照角度都有关系)。NdotL是光照在平面法线方向上的分量,一般用于和光照颜色相乘来得到关于场景里实际灯光的颜色强度。 - 下一步,我们需要计算高光反射(Specular)部分。继续添加下面的代码:
//Create Specular
float NdotH = max (0, dot (s.Normal, halfVec));
float spec = pow (NdotH, s.Specular*128.0) * s.Gloss;布料渲染很大程度上依赖你从什么角度观察这个平面。观察角度越倾斜,就有越多的纤维捕捉到灯光后面的光照,并增强了高光反射。(菲涅耳效应)
//Create Fresnel
float HdotV = pow(1-max(0, dot(halfVec, viewDir)), _FresnelPower);
float NdotE = pow(1-max(0, dot(s.Normal, viewDir)), _RimPower);
float finalSpecMask = NdotE * HdotV - 当大部分计算完成后,我们仅仅需要输出最后的颜色值。添加下面的代码来完成我们的光照模型:
//Output the final color
fixed4 c;
c.rgb = (s.Albedo * NdotL * _LightColor0.rgb)
+ (spec * (finalSpecMask * _FresnelColor)) * (atten * 2);
c.a = 1.0;
return c; - 最后,我们创建surf()函数完成我们的Shader。这里,我们仅仅需要解压法线贴图,并把所有的数据传递给我们SurfaceOutput结构。
void surf (Input IN, inout SurfaceOutput o)
{
half4 c = tex2D (_DetailTex, IN.uv_DetailTex);
fixed3 normals = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)).rgb;
fixed3 detailNormals = UnpackNormal(tex2D(_DetailBump, IN.uv_DetailBump)).rgb;
fixed3 finalNormals = float3(normals.x + detailNormals.x,
normals.y + detailNormals.y,
normals.z + detailNormals.z); o.Normal = normalize(finalNormals);
o.Specular = _SpecWidth;
o.Gloss = _SpecIntesity;
o.Albedo = c.rgb * _MainTint;
o.Alpha = c.a;
}解释:在我们的布料着色器中,我们演示的新技术就是如何使用不同的平铺率整合两个法线贴图。基本的线性代数表明,我们可以将两个向量相加得到一个新的位置。因此,我们可以这样操作我们的法线贴图。我们使用UnpackNormal()函数得到标准变化贴图(Normal Variation map)的法线向量,再将其和细节法线贴图(Detail Normal map)的法线向量相加。这样得到了一个新的法线贴图。然后,我们标准化最后的向量,来让它的范围在0到1之间。如果没有这样做,我们的法线贴图就会看起来就是错的。
Shader "Custom/ClothShader" {
Properties
{
_MainTint ("Global Tint", Color) = (1,1,1,1)
_BumpMap ("Normal Map", 2D) = "bump" {}
_DetailBump ("Detail Normal Map", 2D) = "bump" {}
_DetailTex ("Fabric Weave", 2D) = "white" {}
_FresnelColor ("Fresnel Color", Color) = (1,1,1,1)
_FresnelPower ("Fresnel Power", Range(0, 12)) = 3
_RimPower ("Rim FallOff", Range(0, 12)) = 3
_SpecIntesity ("Specular Intensiity", Range(0, 1)) = 0.2
_SpecWidth ("Specular Width", Range(0, 1)) = 0.2
} SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200 CGPROGRAM
#pragma surface surf Velvet
#pragma target 3.0 sampler2D _BumpMap;
sampler2D _DetailBump;
sampler2D _DetailTex;
float4 _MainTint;
float4 _FresnelColor;
float _FresnelPower;
float _RimPower;
float _SpecIntesity;
float _SpecWidth; struct Input
{
float2 uv_BumpMap;
float2 uv_DetailBump;
float2 uv_DetailTex;
}; inline fixed4 LightingVelvet (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
//Create lighting vectors here
viewDir = normalize(viewDir);
lightDir = normalize(lightDir);
half3 halfVec = normalize (lightDir + viewDir);
fixed NdotL = max (0, dot (s.Normal, lightDir)); //Create Specular
float NdotH = max (0, dot (s.Normal, halfVec));
float spec = pow (NdotH, s.Specular*128.0) * s.Gloss; //Create Fresnel
float HdotV = pow(1-max(0, dot(halfVec, viewDir)), _FresnelPower);
float NdotE = pow(1-max(0, dot(s.Normal, viewDir)), _RimPower);
float finalSpecMask = NdotE * HdotV; //Output the final color
fixed4 c;
c.rgb = (s.Albedo * NdotL * _LightColor0.rgb)
+ (spec * (finalSpecMask * _FresnelColor)) * (atten * 2);
c.a = 1.0;
return c;
} void surf (Input IN, inout SurfaceOutput o)
{
half4 c = tex2D (_DetailTex, IN.uv_DetailTex);
fixed3 normals = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)).rgb;
fixed3 detailNormals = UnpackNormal(tex2D(_DetailBump, IN.uv_DetailBump)).rgb;
fixed3 finalNormals = float3(normals.x + detailNormals.x,
normals.y + detailNormals.y,
normals.z + detailNormals.z); o.Normal = normalize(finalNormals);
o.Specular = _SpecWidth;
o.Gloss = _SpecIntesity;
o.Albedo = c.rgb * _MainTint;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
解释
写在最后
【Unity Shaders】Lighting Models —— 衣服着色器的更多相关文章
- 基于Unity实现油画风格的着色器
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Cust ...
- 基于Unity实现像素化风格的着色器
Shader "MyShaderTest/SimplePixelationShader" { Properties { _MainTex ("Base (RGB)&quo ...
- 【Unity Shaders】Lighting Models 介绍
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- Unity 渲染教程(二):着色器基础
转载:https://www.jianshu.com/p/7db167704056 这是关于渲染基础的系列教程的第二部分.这个渲染基础的系列教程的第一部分是有关矩阵的内容.在这篇文章中我们将编写我们的 ...
- Unity Shader着色器优化
https://mp.weixin.qq.com/s?__biz=MzU5MjQ1NTEwOA==&mid=2247493518&idx=1&sn=c51b92e9300bcf ...
- Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合
一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器 ...
- Shader开发之三大着色器
固定功能管线着色器Fixed Function Shaders 固定功能管线着色器的关键代码一般都在Pass的材质设置Material{}和纹理设置SetTexture{}部分. Shader &qu ...
- 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 & 纹理混合
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星云 ...
- 【Unity Shaders】Lighting Models —— 光照模型之Lit Sphere
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
随机推荐
- ds4700更换控制器导致磁盘无法识别-处理方法
更换DS4700控制器的悲与喜 机型:DS4700 原微码:06.23.xx 更换部件:控制器 (使用的控制器微码07.60.52.00) 误操作过程: 1,关掉存储换控制器 --(兄弟们千万 ...
- swift 初始化
知识点总结: 1.结构体 1.1.默认初始化方法:1.空初始化方法:2.成员初始化方法 struct Person{ var name = "tom" var age = } le ...
- jquery form submit提交后无反应 不报错
jquery form onSubmit默认返回false 因为页面添加了validate验证,在刷新页面后重新提交需要加上下一行代码 onSubmit:function() {return true ...
- 读书笔记-《Maven实战》-2018/4/18
第五章:坐标和依赖 1.每个依赖中可以包含的元素有: groupId,artifactId,version: 这三个元素是Maven项目最重要的元素.Maven需要根据这三个坐标找到需要的依赖. ty ...
- Eclipse创建Maven工程
Eclipse创建Maven工程: Eclipse: New -> Other -> Maven Project -> Next -> webapp -> Finish ...
- python脚本文件传参并通过token登录后爬取数据实例
from bs4 import BeautifulSoup import requests import sys class Zabbix(object): def __init__(self, he ...
- mysql5.7在centos上安装的完整教程以及相关的“坑”
安装前的准备 Step1: 如果你系统已经有mysql,如一般centos自带mysql5.1系列,那么你需要删除它,先检查一下系统是否自带mysql yum list installed | gre ...
- Android开发过程中在sh,py,mk文件中添加log信息的方法
Android开发过程中在sh,py,mk文件中添加log信息的方法 在sh文件中: echo "this is a log info" + $info 在py文件中: print ...
- 【Android】给Android Studio设置代理
先打开我们的Android Studio,点击工具栏的file下的settings,如下图 之后再搜索框上面输入Proxy,然后按第四步提示点击,如下图 之后就进入了设置代理的界面了,如下图 默认情况 ...
- Android的Ui层次
UI 概览 Android 应用中的所有用户界面元素都是使用 View 和 ViewGroup 对象构建而成.View 对象用于在屏幕上绘制可供用户交互的内容.ViewGroup 对象用于储存其他 V ...