Unity中Shader光照模型Blinn-Phong原理及实现

news2024/11/26 10:26:21

文章目录

  • 前言
  • 一、Blinn-Phong原理
  • 二、Blinn-Phong实现
    • 最终代码


前言

Unity中Shader光照模型Blinn-Phong原理及实现,也是经验型光照模型。和Phong模型一样,都是用于实现高光效果


一、Blinn-Phong原理

在这里插入图片描述

可以看出:Blinn-Phong模型和Phong模型不同的地方在于,点积时的 N 和 H 向量

Phong模型:
Specular = SpecularColor * Ks * pow(max(0,dot(R,V)),Shininess)
Blinn-Phong模型:
Specular = SpecularColor * Ks * pow(max(0,dot(N,H)),Shininess)

半角向量的计算方法
在这里插入图片描述
半角向量 = 向量1 + 向量2
即 H = L+ V

二、Blinn-Phong实现

在上一篇 Phong 模型的基础上,进行如下修改即可:

fixed3 H = normalize(L + V);
fixed4 BlinnSpecular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);

先输出一下 BlinnPhong 结果看一下:请添加图片描述

最终代码

Shader "MyShader/P1_5_8"
{
    Properties
    {
        [Header(Diffuse)]
        //光照系数
        _DiffuseIntensity("Diffuse Intensity",float) = 1
        [Header(Specular)]
        //高光颜色
        _SpecularColor("Specular Color",Color) = (1,1,1,1)
        //高光系数
        _SpecularIntensity("Specular Intensity",Float) = 1
        //高光范围系数
        _Shininess("Shininess",Float) = 1
        
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息
                half3 normal:NORMAL;
                
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                //定义一个3维向量,用于接受世界坐标顶点法向量信息
                half3 worldNormal:TEXCOORD1;
                //用于存储模型顶点的世界坐标
                float3 worldPos : TEXCOORD2;
            };

            half _DiffuseIntensity;
            fixed4 _SpecularColor;
            float _SpecularIntensity,_Shininess;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //把顶点法线本地坐标转化为世界坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                //把模型的顶点坐标从本地坐标转化到世界坐标
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //Lambert光照模型的结果
                //Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))
                //使用 Unity 封装的参数 获取环境光色
                float Ambient = unity_AmbientSky;

                //在属性面板定义一个 可调节的参数 用来作为光照系数,调节效果的强弱
                half Kd = _DiffuseIntensity;

                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0;

                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);

                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;

                //使用Lambert公式计算出光照
                //fixed4 Diffuse = Ambient + (Kd * LightColor * dot(N,L));
                //因为 当 顶点法线 与 反射点指向光源的向量 垂直 或成钝角时,光照效果就该忽略不计
                //所以,这里使用 max(a,b)函数来限制 点积的结果范围
                fixed4 Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L));
                
                //return Diffuse;

                //Phong模型公式
                //Specular = SpecularColor * Ks * pow(max(0,dot(R,V)), Shininess)

                // 获取 V (模型顶点的世界坐标 指到 到摄像机世界坐标的单位向量)
                fixed3 V = normalize(_WorldSpaceCameraPos - i.worldPos);
                //使用之前计算得到的公式
                //fixed3 R = 2 * dot(N,L) * N - L;
                //使用自带的计算反射光的函数
                fixed3 R = reflect(-L,N);
                    
                fixed4 Specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(R,V)),_Shininess);

                //BlinnSpecular = SpecularColor * Ks * pow(max(0,dot(N,H)), Shininess)
                fixed3 H = normalize(L + V);
                fixed4 BlinnSpecular = _SpecularColor  * _SpecularIntensity * pow(max(0,dot(N,H)),_Shininess);
                
                return BlinnSpecular+Diffuse;
            }
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            Blend One One
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //加入Unity自带的宏,用于区分不同的光照
            //只声明我们需要的变体
            //#pragma multi_compile POINT SPOT
            
            #pragma multi_compile_fwdadd
            //剔除我们不需要的变体
            #pragma skip_variants DIRECTIONAL POINT_COOKIE DIRECTIONAL_COOKIE
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //使用光照衰减贴图,需要引入 AutoLight.cginc 库
            #include "AutoLight.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //在应用程序阶段传入到顶点着色器中,时加入顶点法向量信息
                half3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                //定义一个3维向量,用于接受世界坐标顶点法向量信息
                half3 worldNormal:TEXCOORD1;
                //定义一个三维向量,用于存放模型顶点 从本地坐标 转化为 世界坐标
                float3 worldPos : TEXCOORD2;
            };

            half _DiffuseIntensity;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                //把顶点法线本地坐标转化为世界坐标
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                //把模型顶点从本地坐标转化为世界坐标
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                /*#if POINT
                return fixed4(0,1,0,1);
                #elif SPOT
                return 0;
                #endif*/

                
                //把模型顶点从世界坐标转化为灯光坐标
                //unity_WorldToLight
                //从世界空间转换到灯光空间下,等同于旧版的_LightMatrix0
                //因为转化时使用的是4行的矩阵,所以 要把模型的顶点坐标增加一个w = 1,使坐标转化准确
                //float3 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1)).xyz;
                //return lightCoord.x;
                //使用Unity自带的光照衰减贴图进行纹理采样
                //fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord));

                //使用Unity自带的方法实现光照衰减
                UNITY_LIGHT_ATTENUATION(atten,0,i.worldPos)

                
                //获取主平行光的颜色
                fixed4 LightColor = _LightColor0 * atten;
                //获取顶点法线坐标(让其归一化)
                fixed3 N = normalize(i.worldNormal);
                //获取反射点指向光源的向量(因为内置了获取的方法,所以不用向量减法来计算)
                fixed3 L = _WorldSpaceLightPos0;
                //因为计算点光源时不需要考虑环境光,所以在Lambert光照模型中删除环境光的影响
                fixed4 Diffuse = LightColor * max(0,dot(N,L));
                
                return Diffuse;
                
            }
            ENDCG
        }
    }
}

最终效果:
请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

排序:如何用快排思想在O(n)内查找第K大元素?

文章来源于极客时间前google工程师−王争专栏。 冒泡排序、插入排序、选择排序三种排序算法,时间复杂度都是O(n^2),比较高,适合小规模数据的排序。 归并排序和快速排序两种时间复杂度O(nlogn)的排序算法,适合大规模的数据排序&am…

未授权和代码执行漏洞特征和检测方法

文章目录 一、Redis未授权访问二、MongoDB未授权访问三、Elasticsearch未授权访问四、Rsync未授权访问五、Windows RDP远程代码执行漏洞(CVE-2019-0708)六、Tomcat Web控制台弱口令七、WebLogic控制台弱口令&反序列化系列漏洞八、WebLogic SSRF(无检…

微服务设计原则:构建弹性和可维护的应用

文章目录 1. 单一职责原则2. 独立性和自治性3. 弹性和容错性4. API 网关5. 日志和监控6. 版本管理7. 自动化部署和持续集成8. 安全性9. 数据一致性10. 文档和通信拓展思考结论 🎉欢迎来到架构设计专栏~微服务设计原则:构建弹性和可维护的应用 ☆* o(≧▽…

Linux环境下Qt应用程序安装器(installer)制作

本文介绍Linux环境下Qt应用程序安装器(installer)的制作。 安装器(installer)是将应用程序安装到操作系统平台的可执行文件,它采用向导式对话框指导用户安装应用程序,如我们在Windows操作系统安装Office软件时,有1个向导让你选择安装哪些组件…

力扣:611. 有效三角形的个数

今日为大家分享一道力扣611有效三角形的个数!本文将会为大家为大家讲解题目,然后算法思路,最后再进行代码的实现!希望看完本文能对读者有一定的收获! 一、题目描述 通过题目的描述可以看出,意思是给定一个…

[产品体验] GPT4识图功能

[产品体验] GPT4识图功能 图片配文字超强的OCR能力知识问答多图解释 打开chatgpt的时候突然发现能用识图了,赶紧去体验一下,大大的震撼… 图片配文字 超强的OCR能力 我传上去的图片并不清晰… 还能准确识别,orz ! 知识问答 多…

代码随想录Day18 LeetCode235 二叉搜索树的公共祖先 T701二叉搜索树中的插入操作 T140 删除二叉搜索树中的公共节点

LeetCode T235 二叉搜索树的公共祖先 题目链接235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode) 题目思路 此题不涉及遍历顺序. 关于二叉搜索树的定义,这里我就不过多赘述了,前面几篇都说清楚了,根节点比左子树元素都大,比右子树元素都小,这道题我们就可…

计算机体系结构和操作系统

这篇文章的主要内容是冯诺依曼计算机体系结构和操作系统的理解。 目录 一.冯诺依曼计算机体系结构 二.操作系统的理解 一.冯诺依曼计算机体系结构 如图是冯诺依曼计算机体系结构,计算机本质就是对数据进行处理的机器,图中,数据从输入设备交给…

VMWare配置桥接

一、设置网络模式 二、编辑网卡配置 ip配置的子网掩码和默认网关保持和宿主机一致,ip局域网内不冲突。 # cd /etc/sysconfig/network-scriptslsvim ifcfg-ens160 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOnone DEFROUTEyes IPV4_FAILURE_FATALno IP…

【数据结构】双链表的相关操作(声明结构体成员、初始化、判空、增、删、查)

双链表 双链表的特点声明双链表的结构体成员双链表的初始化带头结点的双链表初始化不带头结点的双链表初始化调用双链表的初始化 双链表的判空带头结点的双链表判空不带头结点的双链表判空 双链表的插入(按值插入)头插法建立双链表带头结点的头插法每次调…

每日一题 1488. 避免洪水泛滥(中单,贪心,二分)

思路: 当某一天为晴天,可以选择抽水时,我们是不知道要抽哪一个的,最优解应该是抽接下来最近的要发洪水的湖泊,所以我们先把晴天的坐标保存下来,需要用的时候再拿出来需要注意的是,只有晴天发生…

【MATLAB源码-第46期】基于matlab的OFDM系统多径数目对比,有无CP(循环前缀)对比,有无信道均衡对比。

操作环境: MATLAB 2022a 1、算法描述 OFDM(正交频分复用)是一种频域上的多载波调制技术,经常用于高速数据通信中。以下是关于多径数目、有无CP(循环前缀)以及有无信道均衡在OFDM系统中对误码率的影响&am…

Python对接海康威视机器视觉工业相机

一、下载MVS客户端 海康机器人-机器视觉-下载中心 二、解压并安装MVS客户端 三、找到MVS示例代码(代码在MVS的安装位置) 工业相机只允许单条连接,也就是说MVS如果连接了相机,python代码就无法获取数据,此时必须退出M…

计算机毕业设计 大学生选修选课系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

pinctrl子系统 - 架构和结构体关系(四)

一,pinctrl的引入 由于SoC系统越来越复杂、集成度越来越高,SoC中pin的数量也越来越多、功能也越来越复杂,这就对如何管理、使用这些pins提出了挑战。因此,用于管理这些pins的硬件模块(pin controller)就出…

MySQL读写分离之一主一从

原理 MySQL 的主从复制,是基于二进制日志( binlog )实现的。 准备 主机 角色 用户名 密码 192.168.2.3 master root newPwd520 192.168.2.4 slave root newPwd520 主从复制的搭建,可以参考 MYSQL的主从复制-CSDN博客 一主一从读…

【K8S】集群中部署nginx应用 运行手写yaml文件报错排查过程

文章目录 ❌报错信息🔎排查过程✅问题解决 ❌报错信息 提取报错信息【 unknown field “spec.selector.replicas”】【 unknown field “spec.selector.template”】 [rootmaster ~]# kubectl apply -f nginx-deployment.yaml Error from server (BadRequest): erro…

CCF CSP认证 历年题目自练Day30

题目一 试题编号: 202203-1 试题名称: 未初始化警告 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 题目背景 一个未经初始化的变量,里面存储的值可能是任意的。因此直接使用未初始化的变量,比…

通过API接口进行商品价格监控,可以按照以下步骤进行操作

要实现通过API接口进行商品价格监控,可以按照以下步骤进行操作: 申请平台账号并选择API接口:根据需要的功能,选择相应的API接口,例如商品API接口、店铺API接口、订单API接口等,这一步骤通常需要我们在相应…

MyBatis(中)

1、动态sql&#xff1a; 1、if标签&#xff1a; mapper接口: //if 标签 多条件查询List<Car> selectByMultiConditional(Param("brand") String brand,Param("guidePrice") Double guidePrice,Param("carType") String carType); map…