光照模型的概念目前还不明晰,因为笔者也是一个初学者,所以请小心对待笔者介绍的内容。笔者认为光照模型是规定光照算法的模型,比如说前面提到的Lambert光照模型,规定了材质表面的光线的表达式为

环境光+散射光+反射高光+放射光

我们通过一组小实验来说明如何自定义光照模型。

第1.1步:新建一个shader,两个material

其中最后一个是采用了标准的diffuse

MEasyNormalMapping(Material)采用了EasyNormalMapping(shader)。

第1.2步:

▼代码开始
Shader "Custom/EasyNormalMapping" {
Properties {
_Color ("Color", Color) = (,,,)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD CGPROGRAM
#pragma surface surf CustomDiffuse
//还记得吗?这一句的语法是:#pragma surface 光线处理函数 光照模型 sampler2D _MainTex; struct Input {
float2 uv_MainTex;
}; fixed4 _Color; //这个surf非常简单,就是取出了主纹理罢了
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
} /*这里有很多注意事项:
a:下面的LightingCustomDiffuse是CUstomDiffuse这个光照模型的计算光照的实现。
b:声明此光照计算实现函数时,名称必须要按照:Lighting<光照模型名称>,此处即LightingCustomDiffuse
c:光照模型首先经过surf方法得到表面颜色,然后利用LightingCustomDiffuse计算光线,最后呈现在屏幕上
d:这里采用了一个典型的形参列表:(表面颜色 SurfaceOutput s, 光线的来源方向 fixed3 lightDir, 光衰减的系数 fixed atten)
请注意 lightDir是指从表面上的研究点到光源的方向,也就是指向光源方向,而不是入射方向(是入射方向的反方向)
e:返回一个四维的小数,其实是指返回的光线
(其它的形参列表可以看:http://blog.csdn.net/henger_/article/details/52689080)
*/
inline float4 LightingCustomDiffuse(SurfaceOutput s, fixed3 lightDir, fixed atten)
{
//都做了些什么呢?首先是表面法向量和光线方向的点乘,此数目越靠近1,散射越明亮(是不是很符合常理!)
float difLight = max(, dot(s.Normal, lightDir));
//col表示结果的颜色
float4 col; //这里_LightColor0是由Unity根据场景中的光源得到的光源的光色,具体来由不清楚,简单说就是光源的颜色。
//这个公式表明:散射光色 = 表面的反射系数 * 光源 * 散射强度 * 衰减系数(有许多博文说这里要乘以2)
col.rgb = s.Albedo*_LightColor0.rgb*(difLight*atten); col.a = s.Alpha;
return col;
} ENDCG
}
FallBack "Diffuse"
} ▲代码结束

第1.3步:我们为两个材料都选上石头的纹理图,最后可以在预览图中看出,它们的效果完全一样。

总结:我们已经自定义了一个光照模型,这个光照模型和标准内置的diffuse模型效果一样,因为我们的实现原理是一样的。

本文参考了猫都能学会的Unity3D Shader入门指南(二)

第二组实验

halfLambert模型:

将上面的代码稍修改一下,得到HalfLambert模型,这是一个在低光线条件下增亮的做法,是将光强系数 * 0.5+0.5的做法,非常简单。

inline float4 LightingCustomDiffuse(SurfaceOutput s, fixed3 lightDir, fixed atten)

{

float difLight = max(0, dot(s.Normal, lightDir));

float4 col;

//只增加这一行

difLight = difLight * 0.5 + 0.5;

col.rgb = s.Albedo*_LightColor0.rgb*(difLight*atten);

col.a = s.Alpha;

return col;

}

下图中左边应用了HalfLambert模型。

第三组实验:实现积雪效果

第3.1步:新建一个Snow的shader,新建一个名为MSnow的材质。

第3.2步:Snow的代码如下:

▼代码开始
Shader "Custom/Snow" {
Properties{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Bump("Bump", 2D) = "bump"{}
_Snow("Snow Level", Range(,)) = 0.2
_SnowColor("Snow Color", Color) = (,,,)
_SnowDirection("Snow Direction", Vector) = (,,)
//注意U3D中的Y轴是反重力方向的,而不是Z轴。这和UE4不一样。
}
SubShader{
Tags{ "RenderType" = "Opaque" }
LOD CGPROGRAM
#pragma surface surf Lambert sampler2D _MainTex;
sampler2D _Bump;
struct Input {
float2 uv_MainTex;
float2 uv_Bump;
float3 worldNormal; INTERNAL_DATA
/*什么意思?这表示当前点在世界中的法方向,我们采用;INTERNAL_DATA来标记这个特殊的输入*/
//更为详细的参考:https://docs.unity3d.com/Manual/SL-SurfaceShaders.html
};
float _Snow;
float4 _SnowColor;
float4 _SnowDirection; void surf(Input IN, inout SurfaceOutput o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump)); //解释:输入IN表示输入的点(包含一个特殊的输入,就是世界坐标下的法方向)
//WorldNormalVector(IN, o.Normal)表示IN点以及它的法向量在世界坐标下的向量表达,
//和雪的方向的点击,和阈值作比较
//插值函数:lerp(a,b,c)表示(b-a)*c+a
if ( dot( WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) > lerp(, -, _Snow)) {
o.Albedo = _SnowColor.rgb;//取雪色
}
else {
o.Albedo = c.rgb;//取原色
}
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
} ▲代码结束

第3.3步:注意配上法向量和主纹理。

效果:

第四组实验:本组实验修改了顶点,也就是对模型进行了顶点的修改。此前的操作都是在贴图上进行的,顶点没有发生过改动。本组实验强调修改顶点。

第4.1步:在刚刚的Snow(Shader)代码中稍微修改,如下:

▼代码开始
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Custom/Snow" {
Properties{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Bump("Bump", 2D) = "bump"{}
_Snow("Snow Level", Range(,)) = 0.2
_SnowColor("Snow Color", Color) = (,,,)
_SnowDirection("Snow Direction", Vector) = (,,)
//注意U3D中的Y轴是反重力方向的,而不是Z轴。这和UE4不一样。
_SnowDepth("Snow Depth", Range(,0.3))=0.1
//改动1
}
SubShader{
Tags{ "RenderType" = "Opaque" }
LOD CGPROGRAM
#pragma surface surf Lambert vertex:vert
//改动2:在末尾添加vertex:Fun表示顶点模型将修改,修改的方法为Fun
sampler2D _MainTex;
sampler2D _Bump;
struct Input {
float2 uv_MainTex;
float2 uv_Bump;
float3 worldNormal; INTERNAL_DATA
/*什么意思?这表示当前点在世界中的法方向,我们采用;INTERNAL_DATA来标记这个特殊的输入*/
//更为详细的参考:https://docs.unity3d.com/Manual/SL-SurfaceShaders.html
};
float _Snow;
float4 _SnowColor;
float4 _SnowDirection;
float _SnowDepth;
//改动3 void surf(Input IN, inout SurfaceOutput o) {
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump)); //解释:输入IN表示输入的点(包含一个特殊的输入,就是世界坐标下的法方向)
//WorldNormalVector(IN, o.Normal)表示IN点以及它的法向量在世界坐标下的向量表达,
//和雪的方向的点击,和阈值作比较
//插值函数:lerp(a,b,c)表示(b-a)*c+a
if ( dot( WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) > lerp(, -, _Snow)) {
o.Albedo = _SnowColor.rgb;//取雪色
}
else {
o.Albedo = c.rgb;//取原色
}
o.Alpha = c.a;
} //改动4:vert函数处理顶点,对于每一个顶点v,进行如下修改。inout appdata_full v是顶点的固有写法。
void vert(inout appdata_full v) {
/*需求:我们希望vert函数将“法方向和雪方向近似相同的所有顶点都抬高一些”
数学做法:
1:将雪方向(也就是010)转换到物体坐标系中;
这是什么意思?就是以物体来看,雪的方向是什么,如果物体是倒置的(脑子朝下的),那么物体看来的雪方向应该是0,-1,0,
如果物体时脑子靠一侧的,那么物体看的雪的方向将会是水平方向的(它是这样认为的),例如1,0,0, 如何将世界里的雪方向以物体的视角来看待呢?数学上只需要
M(world2object的转换矩阵) * 雪的世界方向 = 雪的物体系下的方向 【语法上】
1.a:unity_ObjectToWorld是内置的一个矩阵,表示此物体到世界的转换矩阵(也就是UE4中的Transform)
1.b:transpose(P)表示将P转置
1.c:mul(P,v)表示矩阵P左乘向量v即Pv 2:上述得到的物体看待的雪方向记为sn,那么sn和此顶点的法方向的点乘,点乘值越大,表示越是容易积雪的情况
如果大于阈值,那么顶点的xyz在积雪方向上增加一定的量,此处的积雪方向是指法+雪的方向
*/ //写法A:
float4 sn = mul(transpose(unity_ObjectToWorld), _SnowDirection); //写法B:sn = _SnowDirection;
/*写法A是正确的,写法B是错误的,它没有将雪的方向转换为物体看待的描述*/ if (dot(v.normal, sn.xyz) >= lerp(, -, (_Snow * ) / )) {
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth;
}
} ENDCG
}
FallBack "Diffuse"
} ▲代码结束

效果可以看出:下面的球中,容易积雪的顶点被抬高了许多。

appdata_full拥有如下成员:

struct appdata_full {

float4 vertex : POSITION;

float4 tangent : TANGENT;

float3 normal : NORMAL;

float4 texcoord : TEXCOORD0;

float4 texcoord1 : TEXCOORD1;

float4 texcoord2 : TEXCOORD2;

float4 texcoord3 : TEXCOORD3;

fixed4 color : COLOR;

UNITY_VERTEX_INPUT_INSTANCE_ID

};

以上顶点的信息中,前四个分别是:点的位置,点的正切方向(也就是定点的正切空间的指向,正切空间就是表面指向外的方向,也就是物理世界中严格的概念“法线方向”),点的法线方向(规定的法线方向,以正切空间为坐标系),纹理信息,以及最后的Color表示顶点的颜色。

——2017年7月28日16:21:45 小江村儿的文杰 zouwj5@qq.com

Unity Shader入门教程(三)自制光照模型的更多相关文章

  1. 【Unity Shader】(三) ------ 光照模型原理及漫反射和高光反射的实现

    [Unity Shader](三) ---------------- 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 ...

  2. Unity Shader入门教程(一)

    参考文献:http://www.360doc.com/content/13/0923/15/12282510_316492286.shtml Unity Shader是着色器,将纹理.网格信息输入,得 ...

  3. Unity Shader入门教程(二)最基本的Diffuse和Normal样例

    本教程参考了<猫都能学会的Unity3dShaderLab教程.CHM>, 1.请上网搜索并下载此文件. 2.随后再下载里面提到的素材: http://vdisk.weibo.com/s/ ...

  4. Unity Shader入门教程(四)反射光斑的实现

    本节内容介绍PhongModel(也就是上文说的反射光的计算模型),采用的计算方法是BlinPhong(也即是用视线方向V+光源方向L的和,与N做点积,随后幂化得到高光反射系数)下图采用了csdn博文 ...

  5. unity shader入门(三)逐像素光照,Blinn-Phong模型

    与上篇逐顶点光照很像,只是改为在片元着色器中计算光照,下为逐像素光照shader Shader "study/Chapter6/PixelShader"{ Properties{ ...

  6. Unity Shader入门精要读书笔记(一)序章

    本系列的博文是笔者读<Unity Shader入门精要>的读书笔记,这本书的章节框架是: 第一章:着手准备. 第二章:GPU流水线. 第三章:Shader基本语法. 第四章:Shader数 ...

  7. Unity Shader入门精要学习笔记 - 第9章 更复杂的光照

    转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...

  8. Unity Shader入门精要学习笔记 - 第7章 基础纹理

    转自 冯乐乐的 <Unity Shader 入门精要> 纹理最初的目的就是使用一张图片来控制模型的外观.使用纹理映射技术,我们可以把一张图“黏”在模型表面,逐纹素地控制模型的颜色. 在美术 ...

  9. Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照

    转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交 ...

随机推荐

  1. CSS3 网格布局(grid-layout)基础知识 - 网格模板属性(grid-template)使用说明

    CSS3引入了新的网格布局(grid layout),以适应显示和设计技术的发展(尤其是移动设备优先的响应式设计). 主要目标是建立一个稳定可预料且语义正确的网页布局模式,用来替代过往表现不稳定且繁琐 ...

  2. 转载 IOS开发之---static变量

    Objective-C 支持全局变量 主要有两种实现方式: (1)第一种和C/C++中的一样, 使用"extern"关键词: (2)另外一种就是使用单例实现. (比如我们经常会把一 ...

  3. WAF 强化学习

    参考:https://github.com/duoergun0729/3book/tree/master/code/gym-waf 代码: wafEnv.py #-*- coding:utf-8 –* ...

  4. antd-iconfont for inner network

    npm install antd-iconfont --save npm install less less-loader --save-dev webpack.config.js const pat ...

  5. L145

    实践是检验真理的唯一标准.Only social practice can be the criterion of truth.工会负责人谴责这一行动破坏了协议.Union officials den ...

  6. 解决webpack vue 项目打包生成的文件,资源文件均404问题

    最近在使用webpack + vue做个人娱乐项目时,发现npm run build后,css js img静态资源文件均找不到路径,报404错误...网上查找了一堆解决办法,总结如下 一.首先修改c ...

  7. Qt中QT_BEGIN_NAMESPACE和QT_END_NAMESPACE的作用

    在Qt中,我们经常会看到 QT_BEGIN_NAMESPACE class QAction; class QMenu; class QPlainTextEdit; QT_END_NAMESPACE 这 ...

  8. Win7系统64位环境下使用Apache——Apache2.4版本安装及卸载

    转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/70255992 本文出自[我是干勾鱼的博客] 之前在Win7系统64位环境下使用A ...

  9. 程序try-catch的绝对健壮性之嵌套

    写程序的过程中,我们对try-catch在熟悉不过了,捕获异常进行处理,以保证程序的健壮性. 今日突发一想,如果我们catch中的代码异常了怎么办?我们做以下一种假设 static void Main ...

  10. rebar安装及创建项目

    rebar作为erlang开发中编译,构建,发布,打包,动态升级的常用工具,下面我记录下rebar工具的安装及使用 从源码安装rebar 1. 建立文件 install_rebar.sh 2. 拷贝如 ...