【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来做 ...
随机推荐
- 解决Android中多次点击(快速点击多次 )启动多个相同界面的问题
通过以下代码可以解决这个问题. /** * 防止快速点击 * @param ev * @return */ @Override public boolean dispatchTouchEvent(Mo ...
- Bitnami Redmine 中文附件名 报错修复
最近自己在服务器上搭了个redmine,用的是Bitnami的一键安装程序. 搭好后,运行得不错,居然还增加了负载均衡. 某天上传中文附件,打开报内部错误,去redmine官网看了下,果然有这个问题, ...
- Yii2 使用 faker 生成假数据(转)
测试过程中有时候需要生成大量的假数据,faker 是一个生成假数据的类库,可以生成姓名,电话,IP地址,密码,ISBN等等你能想到的或者你想不到的各种类型的假数据. Yii2.0已经集成该类库,不用再 ...
- unity, unity默认的Arial字体在编译出的h5版本中不显示
unity默认的Arial字体在编译出的h5版本中不显示.改用自己的字体可显示.
- 关于CALayer 中的contents(图片) 拉伸
最近做有关 发送图片的功能.微信显示的图片 使用气泡遮罩出来的..而且图片尺寸也不止一种. 既然UIImagView 可以拉伸图片,设置遮罩的CALayer 的content 可不可以. 关键CALa ...
- jQuery学习笔记(jquery.form插件)
官网: http://malsup.com/jquery/form/ jQuery Form插件是一个优秀的Ajax表单插件,可以非常容易地.无侵入地升级HTML表单以支持Ajax.jQuery Fo ...
- Merge into 详细介绍
/*Merge into 详细介绍 MERGE语句是Oracle9i新增的语法,用来合并UPDATE和INSERT语句. 通过MERGE语句,根据一张表或子查询的连接条件对另外一张表进行查询, 连接条 ...
- Session的clear方法和flush方法
Session Clear()与 Flush() 解释 1.Clear 方法 无论是Load 还是 Get 都会首先查找缓存(一级缓存) 如果没有,才会去数据库查找,调用Clear() 方法,可以强 ...
- C图形库 Mingw g++ ege Windows下
今天闲来无事就想搞搞图形界面,本来是想弄openGL的但是配置有点麻烦,就觉得先弄个简单的图形库ege了. 1.下载头文件 地址:http://misaka.googlecode.com/files/ ...
- 在sublime text3中安装sass编译scss文件
一 搭建环境 首先安装ruby环境,不然会编译失败,在这里下载ruby ,安装的时候选择第二项 在cmd中输入gem -v 显示版本号说明ruby安装成功 待ruby安装成功后,在cmd中输入 gem ...