UnityShader(十二)实现标准光照模型中的高光反射

目录

基本光照模型中的高光反射公式:

逐顶点光照

逐像素光照


基本光照模型中的高光反射公式:

c_{specular}=\left ( c_{light}\cdot m_{specular} \right )max\left ( 0,\widehat{v},\widehat{r} \right )^{m_{gloss}}

从公式可以看出 要计算高光反射需要知道四个参数:入射光线的颜色和强度clight,材质的高光反射系数mspecular,视角方向v以及反射方向r。其中,反射方向r可以由表面法线n和光源方向l计算得到

即:

\widehat{r}=\widehat{l}-2\left ( \widehat{n}\cdot \widehat{l} \right )\widehat{n}

上述公式很简单,Cg也提供了计算反射方向的函数reflect

函数:reflect(i,n)

参数:i:入射方向;n:法线方向。可以是float,float2,float3等类型。

描述:当给定入射方向i和法线方向n时,reflect函数可以返回反射方向

逐顶点光照

//给Shader命名
Shader "MyShader/SpecularVertex"
{
    //_Specular控制材质的高光反射属性,_Gloss控制高光区域的大小
    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);
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));

                /*对于高光反射部分,先计算入射光线方向关于表面法线的反射方向reflectDir
                由于Cg的reflect函数的入射方向要求是由光源指向交点处,因此需要对worldLightDir取反后再传给reflect函数。
                然后通过_WorldSpaceCameraPos得到世界空间中的摄像机位置,再把顶点位置从模型空间变换到世界空间下,
                再通过和_WorldSpaceCameraPos相减得到世界空间下的视角方向。*/
                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"
}

效果:

 

使用逐顶点的方法得到的效果有较大的问题。我们可以看到高光部分并不平滑,这是因为高光反射部分的计算是非线性的,而在顶点着色器中计算光照再进行插值的过程是线性的,破坏了原计算的非线性关系。因此我们就需要采用逐像素的方法计算高光反射。 

逐像素光照

//给Shader命名
Shader "MyShader/SpecularPiexl"
{
    //_Specular控制材质的高光反射属性,_Gloss控制高光区域的大小
    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 worldNormal:TEXCOORD0;
                fixed3 worldPos:TEXCOORD1;
            };

            //顶点着色器只需要计算世界空间下的法线方向和顶点坐标并传递给片元着色器
            v2f vert(a2v v)
            {
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
             
                return o;
            }

            fixed4 frag(v2f i):SV_TARGET
            {
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal=normalize(i.worldNormal);
                fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));

                fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
                fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir,viewDir)),_Gloss);

                return fixed4(ambient+diffuse+specular,1.0);
            }

            ENDCG
        }    
    }

    FallBack "Specular"
}

效果:

可以看出,按照逐像素的方式处理光照可以得到更加平滑的高光效果。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/362966.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE4 C++ 结构体

先在UCLASS()前写入: USTRUCT(BlueprintType) struct FMyStruct //必须以"F"开头 {GENERATED_BODY() //必须添加“GENERATED_BODY()”UPROPERTY(EditAnywhere, BlueprintReadWrite, Category "MyStruct1")int32 Health;UPROPERTY(EditAnywher…

编程流程图

对于复杂流程,我做开发之前一般会 先画一下流程图。特别是多个部门有交叉的情况下: processOn: 这个是我之前 一直的选择,他可以画上面的这些,流程图,网页操作,但是他不是免费的,查过…

纯血鸿蒙来了,鸿蒙App开发该如何提速

“全世界做产品挣钱的公司很多,但有能力打造操作系统的公司没有几家,最后世界上的操作系统就只有三套:鸿蒙、iOS和安卓。” --- 360集团创始人、董事长周鸿祎 “HarmonyOS实现了AI框架、大模型、设计系统、编程框架、编程语言、编译器等全栈…

力扣经典题目:循环队列

1.虽然是循环队列,但需要提供一个队列为满的情况,所以,要设立一个空的队列元素,当最后一个元素的next指针加一等于第一个元素的时候为满 2.可以增加一个size记录元素个数,当size为0的时候为空,当size为目标…

Kafka-服务端-PartitionLeaderSelector、ReplicaStateMachine

PartitionLeaderSelector 通过对前面的分析可知,PartitionMachine将Leader副本选举、确定ISR集合的工作委托给了PartitionLeaderSelector接口实现,PartitionMachine可以专注于管理分区状态。这是策略模式的一种典型的应用场景。 图展示了PartitionLead…

收集子域名信息(二):第三方网站查询

一、介绍 通过第三方网站查询子域名信息是指使用外部提供的在线工具或服务,通过输入主域名(主网站的域名)来获取与该主域名相关的子域名列表的过程。子域名是在主域名下创建的附加标识,通常用于将网站内容组织成不同部分或为特定…

如何使用mock.js实现接口测试的自动化?

Mock.js 基础用法介绍 Mock.js是一个常用于生成随机数据和拦截Ajax请求的JavaScript库。本文将介绍Mock.js的用法,包括安装和基础用法,在开始前我们可以看下看:了解 Mock.js 的语法规范。 安装 可以通过npm安装Mock.js: npm i…

2024年【汽车驾驶员(高级)】模拟试题及汽车驾驶员(高级)理论考试

题库来源:安全生产模拟考试一点通公众号小程序 汽车驾驶员(高级)模拟试题是安全生产模拟考试一点通总题库中生成的一套汽车驾驶员(高级)理论考试,安全生产模拟考试一点通上汽车驾驶员(高级&…

动画学习:CSP动画制作

会画画就能做手书?!真动画入门教程!【优动漫/CSP教程】https://www.bilibili.com/video/BV1ku411S7Ey/?spm_id_from333.337.search-card.all.click&vd_source124076d7d88eee393a1d8bf6fc787efa 有些人用的是优动漫软件,但是…

现在普通人的消费选择发生了怎样的变化?

消费选择的变化:现代普通人消费观念的演变 随着社会的进步和经济的发展,现代普通人的消费选择发生了翻天覆地的变化。这种变化不仅体现在物质生活的丰富上,更反映在人们消费观念的转变上。 在过去,人们的消费选择相对单一&#…

网络层 IP协议(1)

前置知识 主机:配有IP地址,但是不进行路由控制的设备 路由器:既配置了IP地址,又能进行路由控制的设备 节点:主机和路由器的总称 IP协议主要完成的任务就是 地址管理和路由选择 地址管理:使用一套地址体系,将网络设备的地址描述出来 路由选择:一个数据报如何从源地址到目的地址 …

C++ : 类的简单介绍(四)——析构函数

概念: 与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。 而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。 特征: 1. 析构函数名是在类名前加上字符 ~ 2. …

大数据分析|从七个特征理解大数据分析

文献来源:Saggi M K, Jain S. A survey towards an integration of big data analytics to big insights for value-creation[J]. Information Processing & Management, 2018, 54(5): 758-790. 下载链接:链接:https://pan.baidu.com/s/1…

【Zotero】如何在word文档中插入zotero中的文献

博主最近在用zotero管理文献,并在word文档中写文章,期间需要将zotero生成的参考文献插入文章中,这里将博主遇到的问题以及解决的办法分享给大家,供咱们一起学习。 博主遇到的问题主要有以下几个: 1、参考文献的格式以…

计算机网络——网络层(3)

计算机网络——网络层(3) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)1 网络层——控制平面因特网中自治系统内部的路由选择总括考虑因素总结 ISP之间的路由选择:BGP考虑因素总结 SDN控制层面重要组件和功能总结 ICMP主要功能和特点…

Java强训day13(选择题编程题)

选择题 编程题 题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);String s sc.nextLine();char[] c s.toCharArray();int i 0;int t 0;while (i < c.length) {if (c[i] ! \") {…

Spring速成(一)

文章目录 Spring速成&#xff08;一&#xff09;1&#xff0c;课程介绍1.1 为什么要学?1.2 学什么?1.3 怎么学? 2&#xff0c;Spring相关概念2.1 初识Spring2.1.1 Spring家族2.1.2 了解Spring发展史 2.2 Spring系统架构2.2.1 系统架构图2.2.2 课程学习路线 2.3 Spring核心概…

12nm工艺,2.5GHz频率,低功耗Cortex-A72处理器培训

“ 12nm工艺&#xff0c;2.5GHz频率&#xff0c;低功耗Cortex-A72处理器培训” 本项目是真实项目实战培训&#xff0c;低功耗UPF设计&#xff0c;后端参数如下&#xff1a; 工艺&#xff1a;12nm 频率&#xff1a;2.5GHz 资源&#xff1a;2000_0000 instances 为了满足更多…

C语言第十五弹---操作符(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 操作符 1、操作符的分类 2、二进制和进制转换 2.1、2进制转10进制 2.1.1、10进制转2进制数字 2.2、2进制转8进制和16进制 2.2.2、2进制转16进制 3. 原码、反…

【日常问题】Failed to enable unit: Unit file docker.service does not exist.

Failed to enable unit: Unit file docker.service does not exist. 1. 问题原因 笔者问题的产生是因为在ubuntu20.04下采用snapd安装的docker&#xff0c;因此 systemctl restart docker.servicesystemd并不能找到守护进程docker.service 同时使用docker命令时还会产生若干…