2.4 传统经验光照模型详解

news2024/11/16 19:23:40

一、光照模型

光照模型(illumination model),也称为明暗模型,用于计算物体某点处的光强(颜色值)。从算法理论基础而言,光照模型分为两类:一种是基于物理理论的,另一种是基于经验模型的。

基于物理理论的光照模型:偏重于使用武力的度量和统计方法,效果非常真实,但是计算复杂,实现起来也较为困难。

经验模型:是对光照的一种模拟,通过实践总结出简化的方法,简化了真实光照计算,并能达到不错的效果。

 

1.1 为什么需要光照模型

现实世界的光照是极其复杂的,而且会受到诸多因素影响,有限的计算能力无法完全模拟。使用简化的光照模型对现实的情况进行近似,使得计算处理起来会更容易,并且零效果更符合需求。这些光照模型就是基于我们对光的特性的理解。

1.2 光照模型的发展

二、局部光照模型的组成

定义:局部光照模型只关心直接光照部分,即直接从光源发出并照射到物体表面并反射至摄像头的光线。

组成:

    • 漫反射
    • 高光反射
    • 环境光
    • 自发光

漫反射

定义:在光照模型的定义中,当光线从光源照射到模型表面时,光线均匀被反射到各个方向,这种现象就是漫反射。在漫反射的过程中,光线发生额了吸收和散射,而因此改变颜色方向。

如何计算:漫反射光照符合Lambert定律,反射光强与法线和光源方向之间的夹角余弦值成正比。

基础理论:在光学中通常使用辐射度来量化光。

其中光源每秒发出的能量为辐射通量P

单位面积接受的光量为辐射密度(辐照度)P/A

当光源垂直照射的单位面积的辐照度为E1 = P/A1

将光束以某个角度照射到更大的平面上的辐射度为E2 = P/A2

简洁的来说:反射光线的强度与表面法线和光源方向之间的夹角的余弦成正比。

高光/镜面反射

定义:当光线到达物体表面并发生了反射,观察室现在反射光线的附近时,便能够观察到高光反射。高光反射描述了光线与物体表面发生的反射(光强不变,方向改变)。

高光反射的反射率水根据一种菲尼尔效应的物理现象决定的,通常使用对应的反射贴图描述物体表面的反射率,并且使用光泽度(粗糙度,反光度)描述高光范围的大小。

环境光

定义:在局部光照模型中,由于没有考虑间接光照影响,因此为了处理这种间接光照,从而引入环境光。

计算:

通常使用漫反射的反照率来只是环境光找的反射量,个模型假定场景中发生多次散射和反射,并在所有方向上均等的射向目标物体。

渲染结果:

自发光

定义:顾名思义,物体自身发射的光线,通常作为单独的一项加入光照模型,一般使用一张发光贴图描述物体的自放光。

局部光照模型的整体效果

三、着色模型

1、Gourand模型(逐顶点着色)

定义:以顶点为单位计算光量在通过差值得到每个像素点的光亮度。

在表现物体光滑性上有较好的表现,但值不再是线性变化的时候,比如镜面高光,由于线性差值导致内插值不可能大于顶点值。因此高光只能出现在顶点。

由于使用的颜色差值,会导致镜面高高蔓延到周边。

代码部分:

  • 是在定点着色器中计算的(因为是逐顶点)
  • 顶点颜色使用的是Phong模型的结果
  • 高光部分用的是观察方向和反射方向的余弦值
  • 计算完之后将它座位顶点所携带的颜色
  • 在片元/像素着色器中,直接将顶点携带的颜色赋值给了返回颜色

最终效果:

左边是Gourand模型

2、Flat模型

平面着色模型,计算多边形的单个强度,每个三角形只有一个法线方向。以相同的光强度值显示多边形的所有点。适用于lowpoly风格的场景。

四、经典光照模型

1、Lambert模型

光照公式:

n:表面法线

l:指向光源的单位矢量

mdiffuse:材质的漫反射颜色

clight:光源颜色

防止法线和光源方向点乘的结果为负值,为此使用取最大值的函数来将其截取到0,这样可以防止物体被从后面来的光源照亮。

漫反射效果与观察者位置无关,于光源位置有关。

代码部分:

主光源入射光 * 漫反射材质的颜色 * 法线方向与光照方向的点积

渲染效果:

可以看出漫反射至于光源和表面法线有关,与观察者无关。

2、Phong模型

如何计算:

在这四个矢量中,我们实际上只需要知道其中3个矢量即可,而第四个矢量——反射方向,可以通过其他信息计算得到:

r计算过程参考:几何向量:计算光线反射reflect向量_空间几何中怎么求入射光的反射向量_羊羊2035的博客-CSDN博客

mgloss:材质的光泽度(反光度),它用于控制高光区域的“亮点”有多宽,数值越大,亮点越小。

mspecular:材质的高光反射颜色,它用于控制该材质对于高光反射的强度和颜色。

Clight:光源的颜色和强度

r:反射光线

v:视线向量

反应了光泽度与高光反射的关系

光泽度渲染效果:

最终计算公式:

代码部分:

unity的环境光宏 * 漫反射材质

最终渲染结果:

3、Blinn-Phong模型

在Phong的基础上引入了一个新的矢量h,他是通过vl取平均后在归一化得到的。

然后使用nh之间的夹角进行计算,而非vr之间的夹角。

计算公式:

最终计算公式:

代码部分:

最终渲染结果:

4、Phong和Blinn-Phong模型的区别

通过对Phong模型和Blinn-Phong模型公式对比,可以知道公式的不同之处在于使用了半角向量与发现的点积结果。

半角向量的使用带来的变化:

    • 计算更加简洁,半角向量的计算更加简洁
    • 当光源与视线都在表面之上时,半角向量与发现的角度永远不大于90度

设想一种情况,当材质的反光度非常低时,因此物体被光线照射的大部分区域都会发生高光反射。这些去榆中的一部分高光部分的反射向量与视线的夹角超过了90度。此时,如果使用Phong模型就会导致高光区域一部分发生缺失。这是由于Phong模型只会考虑实现与光照分布在法线两侧的情况。当时视线与光照在发现同侧时且高光反射对亮度有较大影响时,就会发生断层。

五、光照模型展示

作业

说出能量守恒的理念在基础光照模型中的作用

我理解的在光照模型中的能量守恒理念就是:光源所发出的能量(光线)经过物体多次表面反弹、散射、射入等后,其能量总和不会发生变化。

能量守恒是PBR的一个重要概念。它可以保证美术合适的设置材质的反射率和albedo值,而不破坏物理规则。虽然在着色系统中强制能量守恒的物理限制并不等价于最后好看的着色效果,但起码可以使得渲染效果不至于背离物理规则太远,保证在不同光照条件下物体的光照表现一致性。

基于能量守恒的理念,自己写一套完整的光照模型,需要包含环境光照



Shader "Lpk/Blinn-Phong"
{
    Properties
    {
        _Diffuse          ("Diffuse", Color)         = (1,1,1,1)
        _MainTex          ("Texture", 2D)            = "white"{}
        _NormalMap        ("Normal", 2D)             = "pink"{}
        _LocalNormalScale ("LocalNormal",Range(0,1)) = 0
        _Specular         ("SpecularColor", Color)   = (1,1,1,1)
        _Gloss            ("Gloss", Range(1.0,255))  = 20
        _CubeMap          ("Env map",CUBE)           = ""{}
        _EnvScale         ("Env Scale",Range(0,1))   = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //引入头文件
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f
            {
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD2;
                float3 tangentNormal : TEXCOORD3;
                float3 bitangentNormal : TEXCOORD4;
                float3 worldPos : TEXCOORD5;
            };

            sampler2D   _MainTex;
            float4      _MainTex_ST;
            sampler2D   _NormalMap;
            float4      _NormalMap_ST;
            fixed4      _Diffuse;
            fixed4      _Specular;
            float       _Gloss;
            float       _LocalNormalScale;
            samplerCUBE _CubeMap;
            float       _EnvScale;
            
            
            v2f vert (appdata v)
            {
                v2f o;               
                //将顶点从模型空间转换到裁剪空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                //把发现转换到世界空间
                //o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);(旧写法)
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
                //计算UV Offset与Tiling
                o.uv0 = TRANSFORM_TEX(v.uv0,_MainTex);
                o.uv1 = TRANSFORM_TEX(v.uv1,_NormalMap);
                //计算法线切线、副切线
                o.tangentNormal = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz);
                o.bitangentNormal = normalize(cross(o.worldNormal,o.tangentNormal) * v.tangent.w);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //--基础的计算
                //纹理采样
                fixed4 mainTex = tex2D(_MainTex,i.uv0);
                //环境光
                fixed3 amibent = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.rgb * mainTex.rgb;
                //归一化光方向
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                //归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的
                fixed3 worldNormal = normalize(i.worldNormal);
                
                //--计算法线贴图
                fixed3x3 tangentRotation = fixed3x3(i.tangentNormal,i.bitangentNormal,worldNormal);
                fixed3 normalTangent = UnpackNormal(tex2D(_NormalMap, i.uv1));
                //将法线从切线空间转换到世界空间
                fixed3 normalWorld = normalize(mul(normalTangent.rgb,tangentRotation));
                fixed3 finalNormal = lerp(worldNormal,normalWorld,_LocalNormalScale);
                
                //--Lambert
                //计算法线与入射光的点积s
                fixed3 NdotL= max(0, dot(finalNormal, worldLightDir));
                //Lambert颜色(漫反射)
                fixed3 lambertColor = mainTex.rgb * _Diffuse.rgb * _LightColor0.rgb * NdotL;
                
                //--Phong
                //计算反射方向R,worldLight表示光源方向(指向光源),入射光线方向为-worldLightDir
                fixed3 reflectDir = normalize(reflect(-worldLightDir, finalNormal));
                //计算该视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                //计算VdotR,视角与反射光线的点积 
                fixed3 VdotR = max(0,dot(viewDir, reflectDir));
                //计算phong颜色
                fixed3 phong = pow(VdotR,_Gloss)* _Specular.rgb;
                
                //--Blinn-Phong
                //计算半角向量
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                //计算NdotH
                fixed3 NdotH = saturate(dot(finalNormal,halfDir));
                //计算Blinn-Phong
                fixed3 blinnphong = pow(NdotH,_Gloss) * _Specular.rgb;
                
                //漫反射与高光有个过渡
                fixed3 specular = lerp(lambertColor * blinnphong, blinnphong,_Gloss / 255);
                
                //环境贴图
                fixed3 worldRef = normalize(reflect(-viewDir, finalNormal));
                fixed4 reflectColor = texCUBElod(_CubeMap, fixed4(worldRef.rgb,(255 - _Gloss) * 8/(255))) * _EnvScale;
                //环境贴图与漫反射有个过渡
                reflectColor.rgb = lerp(reflectColor * lambertColor.rgb,reflectColor,_Gloss/255);
               
                
                //--最终颜色
                fixed4 finalColor = fixed4(lambertColor + amibent + reflectColor.rgb + specular ,1.0);
                //fixed4 finalColor = fixed4( reflectColor.rgb,1.0);
                return finalColor; 
            }
            ENDCG
        }
    }
}

兰伯特光照模型 Phong光照模型 Blinn-Phong光照模型

环境光贴图 环境光贴图=0.2 环境光贴图=1

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

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

相关文章

《深入浅出Java虚拟机》AIC松鼠活动第五期

1、JAVA虚拟机 1.1什么是java虚拟机 Java虚拟机(Java Virtual Machine,JVM)是一种用于执行Java字节码的虚拟机。它可以将Java源代码编译为字节码,然后在不同的操作系统和硬件平台上运行。作为Java语言的核心组成部分&#xff0…

MySQL和Oracle区别

由于SQL Server不常用,所以这里只针对MySQL数据库和Oracle数据库的区别 (1) 对事务的提交 MySQL默认是自动提交,而Oracle默认不自动提交,需要用户手动提交,需要在写commit;指令或者点击commit按钮 (2) 分页查询 MySQL是直接在SQL…

挖了个漏洞,挣了¥12000!

今天给大家分享一个挖漏洞致富的事情。 0x01 前言 本人,一个热爱生活,热爱网络安全的小青年。在此记录自己日常学习以及漏洞挖掘中的奇思妙想,希望能与热爱网络安全的人士一起交流共勉。 0x02 漏洞背景 一个app,开局一个登录框…

模拟面试题

面试题一 C# 1. 装箱和拆箱是什么? 装箱是把栈空间的数据转移到堆空间上去,值类型传引用类型上去 int i 2; object o i; 拆箱是把堆空间的数据转移到栈空间上去,引用类型传到值类型上去 i (int) o 2. 值和引用类型在变量赋值时的区别是…

在OK3588板卡上部署模型实现人工智能OCR应用

一、主机模型转换 我们依旧采用FastDeploy来部署应用深度学习模型到OK3588板卡上 进入主机Ubuntu的虚拟环境 conda activate ok3588 安装rknn-toolkit2(该工具不能在OK3588板卡上完成模型转换) git clone https://github.com/rockchip-linux/rknn-to…

气象名词解释

文章目录 SAMPSAAMO SAM SAM(Southern Annualr Mode) 南半球环状模,是南半球大气环流和气候变异的一种重要现象。具有如下特点: 主要特点: 赤道附近环流:在 SAM 正相位期间,赤道附近的环流增强,称为正 SA…

Java源码规则引擎:jvs-rules决策流的自定义权限控制

规则引擎用于管理和执行业务规则。它提供了一个中央化的机制来定义、管理和执行业务规则,以便根据特定条件自动化决策和行为。规则引擎的核心概念是规则。规则由条件和动作组成。条件定义了规则适用的特定情况或规则触发的条件,而动作定义了规则满足时要…

IO进程线程day3(2023.7.31)

一、Xmind整理&#xff1a; 文件描述符概念&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;用fread和fwrite实现文件拷贝 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <head.h> int main(int argc, const char…

12-3_Qt 5.9 C++开发指南_创建和使用静态链接库

第12章中的静态链接库和动态链接库介绍&#xff0c;都是以UI操作的方式进行&#xff0c;真正在实践中&#xff0c;可以参考UI操作产生的代码来实现同样的功能。 文章目录 1. 创建静态链接库1.1 创建静态链接库过程1.2 静态链接库代码1.2.1 静态链接库可视化UI设计框架1.2.2 qw…

八大排序算法--快速排序(动图理解)

快速排序 概念 快速排序是对冒泡排序的一种改进。其基本原理是通过选取一个基准元素&#xff0c;将数组划分为两个子数组&#xff0c;分别对子数组进行排序&#xff0c;最终实现整个数组的有序排列。快速排序的时间复杂度最好为O(nlogn)&#xff0c;最坏为O(n^2)&#xff0c;…

5个值得收藏的AI绘画网站,还有国产!

随着科技的发展&#xff0c;设计领域也迎来了科技创新&#xff0c;AI绘画网站便是其中的一个代表&#xff0c;本文精选了4个好用的AI绘画网站与大家分享&#xff0c;一起来看看吧&#xff01; 1、即时灵感 作为一个国产的AI绘画网站&#xff0c;即时灵感支持设计师使用中文语…

Spring | Bean 作用域和生命周期

一、通过一个案例来看 Bean 作用域的问题 Spring 是用来读取和存储 Bean&#xff0c;因此在 Spring 中 Bean 是最核心的操作资源&#xff0c;所以接下来我们深入学习⼀下 Bean 对象 假设现在有⼀个公共的 Bean&#xff0c;提供给 A 用户和 B 用户使用&#xff0c;然而在使用的…

IO流简述

IO流IO流使用场景 什么是IO流常用的IO流字节流字符流缓冲流 BIO、NIO、AIO的区别 IO流 IO流使用场景 如果操作的是纯文本文件&#xff0c;优先使用字符流如果操作的是图片、视频、音频等二进制文件。优先使用字节流如果不确定文件类型&#xff0c;优先使用字节流。字节流是万能…

【LLM系列之指令微调】长话短说大模型指令微调的“Prompt”

1 指令微调数据集形式“花样”太多 大家有没有分析过 prompt对模型训练或者推理的影响&#xff1f;之前推理的时候&#xff0c;发现不加训练的时候prompt&#xff0c;直接输入模型性能会变差的&#xff0c;这个倒是可以理解。假如不加prompt直接训练&#xff0c;是不是测试的时…

linux网络编程--select多路IO转接模型

目录 1.TCP状态转换图 2.端口复用 3.半关闭状态 4.心跳包 5.高并发服务器模型--select 6.提纲总结 学习目录 熟练掌握TCP状态转换图熟练掌握端口复用的方法了解半关闭的概念和实现方式了解多路IO转接模型熟练掌握select函数的使用熟练使用fd_set相关函数的使用能够编写s…

winform学习(3)-----Windows窗体应用和Windows窗体应用(.Net Framework)有啥区别?

1.模板选择 在学习winform的时候总是会对这两个应用不知道选择哪个&#xff1f;而且在学习的时候也没有具体的说明 首先说一下我是在添加控件的时候出现了以下问题 对于使用了Windows窗体应用这个模板的文件在工具箱中死活不见控件。 在转换使用了Windows窗体应用(.NET Fram…

JAVA基础原理篇_1.1—— 关于JVM 、JDK以及 JRE

目录 一、关于JVM 、JDK以及 JRE 1. JVM 2. JDK 3. JRE 二、为什么说 Java 语言“编译与解释并存”&#xff1f; 2.2 将高级编程语言按照程序的执行方式分为两种&#xff1a; 2.2 Java的执行过程&#xff1a; 2.3 所以为什么Java语言“编译与解释"共存&#xff1a…

Quartz中Misfire机制源码级解析

文章目录 前文案例展示Misfire机制1. 启动过程补偿2. 定时任务补偿3. 查询待触发列表时间区间补偿 前文 Misfire是啥意义的&#xff1f;使用翻译软件结果是"失火"。一个定时软件&#xff0c;还来失火&#xff1f;其实在Java里面&#xff0c;fire的含义更应该是触发&…

Ai创作系统ChatGPT源码搭建教程+附源码

系统使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到本系统&#xff01; 更新内容&#xff1a; 同步官方图片重新生成指令 同步官方 Vary 指令 单张图片对比加强 Vary(Strong) | Vary(Subtle) 同步官方 Zoom 指令 单张图片无限缩放 Zoom out 2x | Zoom out 1.5x 新增GP…

Linux学习之信号

trap -l和kill -l都可以列出来信号所有值。 而trap "命令1;命令2;" 信号可以捕捉到信号之后再执行命令1、命令2等命令&#xff0c;这里的命令可以不止两条。 快捷键产生信号 echo "This is a test" > trapTest.txt将This is a test写入到trapTest.t…