【Unity Shader】四、高光反射Specular Shader例子
http://www.cnblogs.com/guxin/p/unity-diffuse-shader-demo.html
在上文中已经学习了漫反射Diffuse Shader和环境光,现在再在此基础上加入高光反射Specular Shader。
高光反射的计算公式:
高光反射Specular = 直射光 * pow(max(0, cos(反射光方向和视野方向的夹角)), 高光反射参数)
当然中间的max(0, cos(反射光方向和视野方向的夹角)部分也可以改用半兰伯特模型以增强背光面的光照效果。
逐顶点高光反射
Shader "Custom/07-Specular Vertex" { // 逐顶点高光反射
Properties{
_Diffuse("Diffuse Color", Color) = (,,,) // 可在编辑器面板定义材质自身色彩
_Gloss("Gloss", Range(,)) = // 高光的参数
}
SubShader{
Pass {
// 只有定义了正确的LightMode才能得到一些Unity的内置光照变量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
// 包含unity的内置的文件,才可以使用Unity内置的一些变量
#include "Lighting.cginc" // 取得第一个直射光的颜色_LightColor0 第一个直射光的位置_WorldSpaceLightPos0(即方向)
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse; // 使用属性
half _Gloss;
struct a2v
{
float4 vertex : POSITION; // 告诉Unity把模型空间下的顶点坐标填充给vertex属性
float3 normal : NORMAL; // 告诉Unity把模型空间下的法线方向填充给normal属性
};
struct v2f
{
float4 position : SV_POSITION; // 声明用来存储顶点在裁剪空间下的坐标
float3 color : COLOR; // 用于传递计算出来的漫反射颜色
};
// 计算顶点坐标从模型坐标系转换到裁剪面坐标系
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是内置矩阵。该步骤用来把一个坐标从模型空间转换到剪裁空间
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// 法线方向。把法线方向从模型空间转换到世界空间
fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); // 反过来相乘就是从模型到世界,否则是从世界到模型
// 光照方向。
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
/*
* 漫反射Diffuse = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩
*/
fixed3 diffuse = _LightColor0.rgb * max(, dot(normalDir, lightDir)) * _Diffuse.rgb; // 颜色融合用乘法
// 反射光的方向
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir)); // 参数:平行光的入射方向,法线方向。而lightDir光照方向是从模型表面到光源的,所以取负数。
// 视野方向 = 摄像机的位置 - 当前点的位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex, unity_WorldToObject).xyz);
/*
* 高光反射Specular = 直射光 * pow(max(0, cos(反射光方向和视野方向的夹角)), 高光反射参数)
*/
fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir, viewDir), ), _Gloss);
// 最终颜色 = 漫反射 + 环境光 + 高光反射
f.color = diffuse + ambient + specular; // 颜色叠加用加法(亮度通常会增加)
return f;
}
// 计算每个像素点的颜色值
fixed4 frag(v2f f) : SV_Target
{
return fixed4(f.color, ); // f.color是float3已经包含了三个数值
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素高光反射
Shader "Custom/08-Specular Fragment" { // 逐像素高光反射
Properties{
_Diffuse("Diffuse Color", Color) = (,,,) // 可在编辑器面板定义材质自身色彩
_Gloss("Gloss", Range(,)) = // 高光的参数
}
SubShader{
Pass {
// 只有定义了正确的LightMode才能得到一些Unity的内置光照变量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
// 包含unity的内置的文件,才可以使用Unity内置的一些变量
#include "Lighting.cginc" // 取得第一个直射光的颜色_LightColor0 第一个直射光的位置_WorldSpaceLightPos0(即方向)
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse; // 使用属性
half _Gloss;
struct a2v
{
float4 vertex : POSITION; // 告诉Unity把模型空间下的顶点坐标填充给vertex属性
float3 normal : NORMAL; // 告诉Unity把模型空间下的法线方向填充给normal属性
};
struct v2f
{
float4 position : SV_POSITION; // 声明用来存储顶点在裁剪空间下的坐标
float3 worldNomal : TEXCOORD0;
float3 worldVertex : TEXCOORD1;
};
// 计算顶点坐标从模型坐标系转换到裁剪面坐标系
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是内置矩阵。该步骤用来把一个坐标从模型空间转换到剪裁空间
// 法线方向。把法线方向从模型空间转换到世界空间
f.worldNomal = mul(v.normal, (float3x3)unity_WorldToObject); // 反过来相乘就是从模型到世界,否则是从世界到模型
f.worldVertex = mul(v.vertex, unity_WorldToObject).xyz;
return f;
}
// 计算每个像素点的颜色值
fixed4 frag(v2f f) : SV_Target
{
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
// 法线方向。
fixed3 normalDir = normalize(f.worldNomal); // 单位向量
// 光照方向。
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
// 漫反射Diffuse颜色 = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩
fixed3 diffuse = _LightColor0 * max(, dot(normalDir, lightDir)) * _Diffuse.rgb; // 颜色融合用乘法
// 反射光的方向
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir)); // 参数:平行光的入射方向,法线方向。而lightDir光照方向是从模型表面到光源的,所以取负数。
// 视野方向 = 摄像机的位置 - 当前点的位置
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldVertex);
/*
* 高光反射Specular = 直射光 * pow(max(0, cos(反射光方向和视野方向的夹角)), 高光反射参数)
*/
fixed3 specular = _LightColor0.rgb * pow(max(dot(reflectDir, viewDir), ), _Gloss);
// 最终颜色 = 漫反射 + 环境光 + 高光反射
fixed3 tempColor = diffuse + ambient + specular; // 颜色叠加用加法(亮度通常会增加)
return fixed4(tempColor, ); // tempColor是float3已经包含了三个数值
}
ENDCG
}
}
FallBack "Diffuse"
}
效果比较
如下图,左边是逐顶点计算,右边是逐像素计算。能明显看出来逐顶点计算的光斑块状明显,效果不如逐像素计算的好。

使用Blinn-Phong光照模型
Blinn光照模型:
Specular = 直射光 * pow( max(cosθ,0),10) θ:是反射光方向和视野方向的夹角
Blinn-Phong光照模型:
Specular = 直射光 * pow( max(cosθ,0),10) θ:是法线和x的夹角 x:是平行光和视野方向的平分线
所以只需修改上面例子中的公式计算部分即可。
// 光照方向和视野方向的夹角的平分线
fixed3 halfDir = normalize(lightDir + viewDir); fixed3 specular = _LightColor0.rgb * pow(max(dot(normalDir, halfDir), ), _Gloss);
改用Blinn-Phong光照模型后能看到高光光斑变得更圆了一些,效果如下图:

下图左边是Blinn-Phong模型,背光面不会出现高光,感觉效果更好。

学习资料:
- 逐顶点计算高光反射 http://www.sikiedu.com/course/37/task/443/show#
- 逐像素计算高光反射 http://www.sikiedu.com/course/37/task/445/show
- 使用Blinn-Phong光照模型 http://www.sikiedu.com/course/37/task/446/show
【Unity Shader】四、高光反射Specular Shader例子的更多相关文章
- 【Unity Shader学习笔记】Unity光照基础-高光反射
1.原理 1.1.Phong模型 计算高光反射需要表面法线.视角方向.光源方向.反射方向等. 在这四个矢量中,我们实际上只需要知道其中3个矢量即可,而第4个矢量(反射方向r)可以通过其他信息计算得到: ...
- 【Unity Shader】(三) ------ 光照模型原理及漫反射和高光反射的实现
[Unity Shader](三) ---------------- 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 ...
- Unity shader学习之高光反射光照模型
高光反射光照模型的公式如下: Cspecular = Clight * mspecular * max(0, dot(v, r))gloss 要计算高光反射需要知道4个参数:入射光线颜色Cspecul ...
- Unity Shader (三)Surface Shader机制
转自:http://blog.csdn.net/candycat1992/article/details/39994049 写在前面 一直以来,Unity Surface Shader背后的机制一直是 ...
- 【Unity Shaders】Mobile Shader Adjustment —— 为手机定制Shader
本系列主要參考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同一时候会加上一点个人理解或拓展. 这里是本书全部的插图.这里是本书所需的代码和 ...
- 【Unity Shaders】Shader学习资源和Surface Shader概述
写在前面 写这篇文章的时候,我断断续续学习Unity Shader半年了,其实还是个门外汉.我也能体会很多童鞋那种想要学好Shader却无从下手的感觉.在这个期间,我找到一些学习Shader的教程以及 ...
- Phong & BlinnPhong Specular Shader
[Phong Specular Shader] 如果物体离摄像机很远,或者不需要高精度镜面反射,则Phong模型适用. Phong模型如下: 使用前必须指定使用自定义Phong. [BlinnPhon ...
- 关于Unity中的模型描边与Shader切换(专题二)
模型描边 1: LOL里面的模型描边效果,点击防御塔会有描边的效果,被攻击的时候模型也要描边凸显一下2: 网上可以找到模型描边的Shader,可以直接下载使用,一组第三方的Shader, 帮我们解决了 ...
- Unity GeometryShader(从一个线框渲染的例子开始)
GeometryShader这个概念,已经出现很久了,但由于性能不佳,所以使用的并不多.甚至移动平台根本就不支持.移动平台的硬件更新速度也是越来越快,GS的应用普及应该不会太远.就现阶段而言,GS来做 ...
随机推荐
- 《JAVA与模式》之迭代器模式
定义:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节. 类型:行为类模式 类图: public static void main(String[] args) { List< ...
- MongoDB学习笔记(3)--删除数据库
MongoDB 删除数据库 语法 MongoDB 删除数据库的语法格式如下: db.dropDatabase() 删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名. 实例 以下 ...
- MySQL -- 全文检索
mysql支持全文索引和全文检索--全文索引的索引类型是fulltext--全文索引只能用于innodb表和myisam表,对应的列类型只是支持char.varchar.text--mysql5.7. ...
- 【转】Java 有值类型吗?
Java 有值类型吗? 有人看了我之前的文章『Swift 语言的设计错误』,问我:“你说 Java 只有引用类型(reference type),但是根据 Java 的官方文档,Java 也有值类型( ...
- Cassandra 2.x 提示“错误: 代理抛出异常错误: java.lang.NullPointerException”
这个问题多半是由于运行了多个Cassandra实例造成的错误,看cassandra的启动脚本中可发现这样的语句: # see CASSANDRA-7254 "$JAVA" -cp ...
- Nuxt 开发环境不支持ip访问?
传送门:https://nuxtjs.org/faq/host-port 开发模式下不支持ip访问? 打开package.json,添加如下配置,然后重启即可. "config": ...
- 给现有MVC项目增加Web API支持
在MVC4中自带了Web API不再需要从Nuget中下载. Step1:增加System.Web.Http,System.Web.Http.WebHost,System.Net.Http三个程序集的 ...
- 一条SQL语句中算日销售额和月销售额
刚刚做项目的时候用到的 用户表:用户ID,用户名,余额 流水表:时间,用户ID,用户名,类型(0充值,1消费),变更金额 现在要查每个用户的日销售额和月销售额,本来最简单的方法是先把所有用户查出来,然 ...
- Java常考面试题(五)
序言 好好努力. ---WH 一.Iterator和ListIterator的区别是什么? 自我解答: Iterator是针对所有collection来使用的,而看名字ListIterator,顾名思 ...
- Vivado+FPGA:如何使用Debug Cores(ILA)在线调试(烧录到flash里可以直接启动)
在Vivado下在线调试是利用ILA进行的,Xilinx官方给出了一个视频,演示了如何使用Vivado的debug cores,下面我根据这个官方视频的截图的来演示一下: 官方的视频使用的软件版本为2 ...