前言

此文及专栏系是以Shader入门精要为基础整理的Unity Shader学习笔记,尽量以初学者视角还原(其实半年前我就是初学者),错误还需指正。

本篇是实操部分的第三个Shader,即高光反射Shader,文章选取顶点着色器生成的高光反射Shader作为说明,具体名词可能不再解释。

高光反射模型

本篇实现的是高光反射这一渲染中的重要主题,这种效果通常出现在金属、镜面反射等场合。按照图形学中基础的光照模型,高光反射的光强及色彩由以下公式决定:

从左到右的参数分别是:最终结果,即高光反射的光强和色彩;入射光线的颜色和强度;高光反射系数;max函数的运算结果,是对观察视角向量v和反射方向向量r的取正。

材质的光泽度,这可能是大多数人不曾了解过的名词,事实上我尝试如下理解,请看下面这个光球,如果无视右下角的光泽,那么大亮点就是我们的高光部分。想象一下,亮点的中心那里反射光线正好与观察视角向量重合,也即我们的“眼睛”正好接收到那里的反射光线,v和r单位向量乘积为1;而偏移了那里,反射光线就和我们的视角有夹角了,v和r这两个单位向量的乘积逐渐减小,因为光泽度作为指数存在,这个结果逐渐接近0,也就是光线减弱趋向于消失。

当然,反射方向我们还需要计算,其公式为:

这一效果可以由CG语言的相关函数得到。其中n和I向量分别为法线方向和光源入射方向。

高光反射Shader

接下来正式撰写我们的Shader,仍然基于2020版本的Unity,实测可以渲染成功,环境配置不再赘述,全部代码如下:


Shader "Unlit/HighLightShader"
{Properties {_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)_Specular ("Specular", Color) = (1, 1, 1, 1)_Gloss("Gloss", Range(8.0, 256)) = 20    //这是光泽度}SubShader {Pass{Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Diffuse;fixed4 _Specular;float _Gloss;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;fixed3 color : COLOR;};v2f vert (a2v v){//这是反射部分计算,参考上一篇v2f o;o.pos = UnityObjectToClipPos(v.vertex);fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);fixed halfLambert = dot(worldNormal, worldLightDir)*0.8+0.2;fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;//接下来正式计算高光部分fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)),_Gloss);o.color = ambient + diffuse +specular;return o;}fixed4 frag (v2f i) : SV_Target{// 片元着色器,简单传参return fixed4(i.color, 1.0);}ENDCG}}Fallback "Specular"
}

Properties块参数引用

区别于简单的漫反射,我们的高光Shader不能再使用基础的Color属性参数了,这里提供给材质方面的自定义接口还包括反射颜色和光泽度。

    Properties {_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)_Specular ("Specular", Color) = (1, 1, 1, 1) //这是反射色彩纹理_Gloss("Gloss", Range(8.0, 256)) = 20    //这是光泽度}

我们传入一个Color属性参数,用于反射色彩(为什么要专门新建一个变量,区别于漫反射色彩?我的理解是这玩意和漫反射还是不一样的,比如玻璃和一些镀膜金属就有和表面不同的反射色彩,而且这也方便了游戏美术的运用),至于光泽度Gloss采用float类型,我们这里用Range函数定义,这个函数一看就懂,表示数值范围是8到256(我们前面对光泽度有解释,这个参数放在公式指数位置,因此必然是一个远大于1的值)

格式语言

    SubShader {Pass {Tags { "LightMode"="ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Diffuse;fixed4 _Specular;float _Gloss;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;fixed3 color : COLOR;};

这里不再多解释了,tags表示Pass块在渲染管线中的位置,#pragma用法指定着色器函数,#include包含内置库以使用参数,然后再把properties中的参数重新声明;a2v和v2f分别是我们顶点着色器的输入和输出结构体,区别于上一篇漫反射Shader中计算大部分在片元着色器,我们这里计算主要在顶点着色器完成,所以需要把色彩信息color也传出。

顶点着色器

v2f vert (a2v v){//这是反射部分计算,参考上一篇v2f o;o.pos = UnityObjectToClipPos(v.vertex);fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);fixed halfLambert = dot(worldNormal, worldLightDir)*0.8+0.2;fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;//接下来正式计算高光部分fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)),_Gloss);o.color = ambient + diffuse +specular;return o;}

顶点着色器的内容分两部分,反射部分和高光部分。

反射部分参考上一篇文章,其原理相近。首先拿到Unity计算好的输入结构体,使用UnityObjectToClipPos裁剪vertex(实际上是POSITION顶点位置)(裁剪可以理解为过滤掉不会被渲染的点),然后通过内置的变换矩阵unity_WorldToObject将这一坐标转换成世界坐标系,最后用normalize将其单位化

UNITY_LIGHTMODEL_AMBIENT是内置变量,代表系统接收到的环境光部分(封装相当好,不必关心实现)。_LightColor0和_WorldSpaceLightPos0分别是光照的光量(色彩和光强)和方向向量信息,我们将这些参数引用,并对光照方向向量进行单位化。(xyz自然就是坐标啦)

接下来使用了修正过的半兰伯特光照模型,我们上次提到,纯粹按照漫反射模型计算,因为没有考虑到反射,物体的阴影部分是纯黑的。半兰伯特模型本来是对光照和物体法线两个向量的运算结果,做一个线性变换,也就是dot(worldNormal, worldLightDir)*0.5+0.5,保证不会出现纯黑,但是这样会导致我们最后渲染出的高光不明显,不利于使用,因此修正为dot(worldNormal, worldLightDir)*0.8+0.2。最后_LightColor0.rgb * _Diffuse.rgb * halfLambert这个式子计算出漫反射结果

接下来就是高光部分了,根据公式

我们用reflect(-worldLightDir, worldNormal) 计算出反射向量(我们不关心封装好的函数,但可以知道原理,不是吗),这里的worldLightDir加了个负号取反了,因为reflect函数的入射光线要求是光照点指向光源,而unity提供的_WorldSpaceLightPos0是反向的,需要反着来。

我们来看公式,此时,我们已经拿到了(_LightColor0,内置参数)、(_Specular.rgb,properties引入的参数)、反射向量和观察视角向量,_LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)),_Gloss)这个式子计算出最后的高光反射结果(pow是指数运算函数),然后与漫反射结果相加,输出即可。

片元着色器和结束

            fixed4 frag (v2f i) : SV_Target{// 片元着色器,简单传参return fixed4(i.color, 1.0);}ENDCG}}Fallback "Specular"

前面提过,这个Shader没用片元着色器进行计算,因此frag函数的内容非常简单,加一个默认值1.0的分量即可。最后Fallback里面用Specular,因为我们这是高光反射Shader嘛,应该拿unity的反射Shader作为备份。

总结

最后的渲染效果是右边这样,左边则是上一篇的漫反射。你看,是不是很像unity默认的那种,感觉有点油腻的反射。事实上不必嫌弃它,我们已经实现了基本的高光反射,预设的一些目标也已经完成,比如我们修正的半兰伯特模型也保证了背后不至于全黑。

Unity Shader知识点(三)高光反射Shader相关推荐

  1. Unity Shader标准光照模型——高光反射

    高光反射 顶点高光反射 Shader "Unlit/HighLightReflection" {Properties{_diffuse("diffuse",Co ...

  2. unity shader入门精要_Unity Shader 入门(二):shader 基础

    一.参考与说明(需要写在开始东西): 1.1 Unity Shader 入门精要学习 https://github.com/candycat1992/Unity_Shaders_Book/tree/u ...

  3. 10.高光反射Blinn

    高光反射的公式: Specular = 直射光 * pow(cosθ,高光的参数) θ是反射光方向和视野方向的夹角 由图像看出高光参数可控制光的可视范围 逐顶点的高光反射 // Upgrade NOT ...

  4. shader基础学习摘要(三)高光反射

    高光反射 理论推导 代码实践 Phone光照模型 逐顶点 逐像素 Blinn-Phong光照模型 内置函数 理论推导 在6.2.4节中,我们给出了基本光照模型中高光反射部分的计算公式 高光反射求的夹角 ...

  5. Unity Shader学习记录(6) —— 高光反射光照模型和内置计算函数

    1 高光反射光照模型计算公式 从公式可以看出,要计算高光反射需要知道 4 个参数:入射光线的颜色和强度c,材质的光反射系数 m,视角方向v以及反射方向r.其中,反射方向r可以由表面法线n和光源i计算得 ...

  6. Unity Shader学习-高光反射

    Unity Shader学习-高光反射 高光反射计算公式 高光反射 = 光源的色彩和强度 * 材质的高光反射系数 * pow(max(0,视角方向 · 反射方向),_Gloss) 视角方向 = ref ...

  7. Unity Shader中的基础光照与标准光照模型(自发光、环境光、高光反射、漫反射)、BRDF光照模型

    我们是如何看到这个世界的 从宏观上来说,渲染包含了两大部分 决定一个像素的可见性 决定这个像素上的关照计算 而光照模型就是用于决定一个像素上进行怎样的光照计算 通常来讲,我们要模拟真实的光照环境来生成 ...

  8. Unity Shader 高光反射光照模型

    高光反射光照模型计算公式如下: 需要四个参数:入射光线的颜色和强度,材质的高光反射系数,视角方向,反射方向. 反射方向可以由表面法线和光源方向计算而得: 也可以直接使用CG提供的计算反射方向的函数re ...

  9. 【Unity3D Shader编程】之五 圣诞夜篇 Unity中Shader的三种形态对比 混合操作合辑

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本系列文 ...

最新文章

  1. java web项目调用rest接口404
  2. ECLIPSE远程调试出现如下问题 ECLIPSE中调试代码提示找不到源
  3. poi设置单元格格式为文本_身份证号乱码?日期显示不对?都是单元格格式的锅...
  4. “别人家的小孩”是如何用一行代码手撕面试题的?
  5. ubuntu 修改hostname
  6. python:爬虫初体验
  7. [Python语音识别项目笔记] 3softmax函数
  8. Android AsyncTask两种线程池分析和总结
  9. StringBuffer和StringBuilder使用方法比較
  10. 此加载项为此计算机的所有用户安装_MDI Jade 6.5软件安装教程
  11. geoserver集成以及部署arcgis server瓦片数据
  12. Android 百度地图开发问题----解决地图有时候加载不出来问题
  13. mybatis xml sql
  14. bootstrap - 弹出层
  15. (转)UIPageControl使亮点直接跳到点击dot上
  16. 【Mybatis笔记】mybatis实现mysql增删改查
  17. 数据建模讲解和案例分析
  18. 二维码:MP3音频世界的进化
  19. word插入图片显示不全
  20. 2020年中考英语计算机考试,2020年中考英语听说测试考生问答

热门文章

  1. 项目管理中对个人能力的要求
  2. 12月黑科技资源大全
  3. 数学符号π (Pi)、Σ(Capital Sigma)、μ (Mu) 、σ(sigma)、∏(capital pi), ∫(Integral Symbol)的来历...
  4. Android 购物车的简单实现
  5. [OC学习笔记]系统框架
  6. 苹果耳机插入电脑必须按着通话键才有声音---设置左右音频平衡器解决
  7. python0到7能组成的奇数个数_Python实践|输出0-7组成八位奇数总数
  8. ASP.NET Core Filter
  9. 2021-06-29ansible解决python版本依赖
  10. 漫漫Java学习路,第四天