Unity | Shader基础知识(第八集:案例<漫反射材质球>)

目录

一、本节介绍

1 上集回顾

2 本节介绍

二、什么是漫反射材质球

三、 漫反射进化史

1 三种算法结果的区别

2 具体算法

2.1 兰伯特逐顶点算法

a.本小节使用的unity自带结构体。

b.兰伯特逐顶点算法公式

c.代码实现——兰伯特逐顶点算法

2.2 代码实现——兰伯特逐像素算法

a.像素和顶点算法的区别

b.实现代码

 2.3 代码实现——半兰伯特算法

a.为什么会出现半兰伯特

b.半兰伯特公式

c.代码实现

四、下集介绍


一、本节介绍

1 上集回顾

本集讲了如何让图片和外部颜色叠加显示。

2 本节介绍

如何做一个漫反射材质球。

二、什么是漫反射材质球

1 之前的颜色材质球

我们目前只学过直接上色的材质球(如图1所示),还有上节课的颜色和图片叠加的材质球。

图1 材质球

2 现实的光照下的球

现实光照下的大部分材质球并不是纯色且全亮的,而是(如图2所示)。

图2 现实中的球

 这种模拟大部分现实世界物体发光的状态,就是漫反射材质球。

备注:

反射有两种:镜面反射和漫反射。像镜子的反光,非常光滑的物体反光(比如金属),属于镜面反射,其他大部分是漫反射。具体区别详见初中物理~自己百度哦o(* ̄︶ ̄*)o

三、 漫反射进化史

我们算到最后,对屏幕来说,仅仅想知道,我这个点应该用什么颜色。

所以,对这个颜色的计算出现了三种解法。

  • 兰伯特逐顶点算法
  • 兰伯特逐像素算法
  • 半兰伯特算法

备注:兰伯特是个人,他和别人一起研究出来了以上三个定律。

1 三种算法结果的区别

兰伯特逐顶点算法(白色和黑色交界处有些方块块的感觉、照不到的地方全黑)

兰伯特逐像素算法(白色和黑色交界处平滑过渡、照不到的地方全黑)

半兰伯特算法(白色和黑色交界处平滑过渡、照不到的地方不是全黑)

内容参考(侵权立删):

Unity Shader 漫反射(Lambert、Half Lambert) - 知乎

图3 三种算法得到的效果

2 具体算法

2.1 兰伯特逐顶点算法
a.本小节使用的unity自带结构体。
struct appdata_full {
    float4 vertex : POSITION;    //顶点坐标
    float4 tangent : TANGENT;    //切线
    float3 normal : NORMAL;      //法线
    float4 texcoord : TEXCOORD0;    //第一纹理坐标
    float4 texcoord1 : TEXCOORD1;//第二纹理坐标
    float4 texcoord2 : TEXCOORD2;//第三纹理坐标
    float4 texcoord3 : TEXCOORD3;//第四纹理坐标
    fixed4 color : COLOR;        //顶点颜色
    UNITY_VERTEX_INPUT_INSTANCE_ID    //ID信息
};
b.兰伯特逐顶点算法公式


公式解释:

屏幕上对应点的颜色 = (光的颜色*物体的颜色)*max(0,该点的法向量*该点的光照方向)


备注(max函数解释):

max(a,b),如果这里面a大,答案就是a

如果b大,答案就是b。

例:

max(5,20)=20

max(8,-9)=8

此处的作用:

因为颜色没有负数,如果n*l算出来小于0的时候,就直接为0,其他时候就是n*l的值。

其实就是起一个“一刀切”掉负数的作用。


得出结论:我们想计算漫反射的时候屏幕显示什么颜色,我们需要光的颜色物体的颜色该点的法向量(单位向量)该点的光照方向(单位向量)

备注:公式里的字母上带^就是单位向量的意思。

c.代码实现——兰伯特逐顶点算法

计算注意事项:

在计算n*l时,注意:该点的法向量(往往直接获取的是物体本地坐标),该点的光照方向(往往获取的是世界坐标)

这样是不能乘的,所以需要把他们都换算到一个坐标系,这里换算到世界坐标下。

会用到的方法:

UnityObjectToWorldNormal()     //把物体的法线坐标,换算到世界坐标下
normalize()                    //把任何一个向量变成单位向量
dot()                          //点乘
max()                          //上文讲过

_WorldSpaceLightPos0           //世界坐标下的光线坐标
                               //但是要引用#include "Lighting.cginc"才能找到

 实现的代码:

 SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            
            //新的引用
            #include "Lighting.cginc"

            //返回结构体        //引用结构体
            appdata_full vert (appdata_full v)
            {    
                //模型顶点坐标转屏幕坐标
                v.vertex = UnityObjectToClipPos(v.vertex);
                
                //获取法线坐标并转换成世界坐标下的法线坐标
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);

                 //世界坐标下的光线坐标  //单位化坐标   //获取世界坐标下的光线坐标
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                
                 //上面的公式
                float3 diffuse =_LightColor0.rgb * v.color.rgb * max(0,dot(worldNormal,worldLight));

                //算出的值给颜色
                v.color = float4(diffuse,1);
                
                return v;
            }


            float4 frag (appdata_full v) : SV_Target
            {    
                //输出颜色    
                return float4(v.color,1) ;
            }
            ENDCG
        }
    }
2.2 代码实现——兰伯特逐像素算法
a.像素和顶点算法的区别
  • 从写法角度来看,顶点算法是在顶点着色器中写的,像素算法是在片元着色器中写的。
  • 从原理角度来说,因为顶点是初始值,经过一系列计算后,数据就会和我们想要的有些偏差。

例:让你拿笔写一个字,你可能就写了,但是让你拿竹竿上面绑个中性笔写字,你就写不准了,肯定是离画出来的地方越近,画出来越是自己想要的。

结论:像素着色器离最后的显示比较近,所以出来的结果和我们想要的更一致。

b.实现代码
Shader "Unlit/005_1"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            appdata_full vert (appdata_full v)
            {   
                v.vertex = UnityObjectToClipPos(v.vertex);

                //把法线转换成世界坐标,传进去
                v.normal = UnityObjectToWorldNormal(v.normal);
                return v;
            }

            float4 frag (appdata_full v) : SV_Target
            {
                //法线世界坐标
                float3 worldNormal = v.normal;
                //光线世界坐标
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                //计算颜色
                float3 diffuse =_LightColor0.rgb * v.color.rgb * max(0,dot(worldNormal,worldLight));
                
                //把颜色传进去
                return float4(diffuse,1) ;
            }
            ENDCG
        }
    }
}
 2.3 代码实现——半兰伯特算法
a.为什么会出现半兰伯特

兰伯特的两个算法,得到的球,在没有光线照射的时候都是黑色的,但玩游戏的时候往往希望,虽然光线无法照到,但我们可以看见。

数学知识:公式中的n*l值的范围是【-1,1】之间,我们希望把这个区间改成【0,1】(前面的课学过),【-1,1】*0.5+0.5,就可以转成【0,1】,0的时候就是之前光照模型中黑色部分,越靠近1越亮。

因为我们实际上并不是需要它看不见,只是需要它要明暗变化,所以我们在环境光的基础上加上兰伯特公式计算出的值,就有了明暗变化。

于是就出现了第三种,半兰伯特。

b.半兰伯特公式

在上图基础上:

最终颜色  = 环境光+Cdiffuse

c.代码实现

这里其他代码都没有变,只更改了上图0.5的部分。最后输出前,再加入环境光。

备注:

获取环境光强度的方法:UNITY_LIGHTMODEL_AMBIENT.xyz

Shader "Unlit/005_2"
{
     SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            appdata_full vert (appdata_full v)
            {
                v.vertex = UnityObjectToClipPos(v.vertex);
                v.normal = UnityObjectToWorldNormal(v.normal);
                return v;
            }

            float4 frag (appdata_full v) : SV_Target
            {
                float3 worldNormal = v.normal;
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                //本节变动
                //获取环境光
                float3 anbient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //计算范围
                float halfLamient = dot(worldNormal,worldLight)*0.5+0.5;

                //计算反射强度
                float3 diffuse =_LightColor0.rgb * v.color.rgb *halfLamient;

                //反射光加光照强度
                float3 c = anbient + diffuse;

                return float4(c,1) ;
            }
            ENDCG
        }
    }
}

四、下集介绍

本集讲了3种计算反射光的方法。

下集讲光照计算,高光反射。(最晚更新日期,1月7日)

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

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

相关文章

如何开启In-sensor zoom 功能

和你一起终身学习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一、In-sensor zoom 概述二、如何开启 In-sensor zoom2.1 开启 camxsettings.xml setting2.2 多摄像头,需要添加特殊的逻辑2.3 在 MetaTran…

记录下IAP升级将APP程序修改正常模式下载失败 No Algorithm found for: 08000000H - 08008FFFH

移植发现问题: No Algorithm found for: 08000000H - 08008FFFH 今天在调试程序时,需要把钱同事程序的APP修改成成正常下载就可以用的程序,工程的地址复位也把APP的偏移地址去掉,理论上这样就OK了 偏移地址设置也屏蔽了 STLINK下…

美摄AE模板插件工具,将美摄SDK和AE极致融合

视频内容已经成为企业宣传和品牌建设的重要手段,为了满足企业对于高质量视频制作的需求,美摄科技推出了一款创新性的插件工具——美摄AE模板插件工具。这款工具将美摄SDK能力和Adobe After Effects极致融合,为企业提供了一种快速制作和转化美…

vue 历程记

目录 前言一、源码优化1、vue3.x 采用 monorep 的理念来管理源码2、vue3.x 源码采用 TypeScript 开发 二、性能优化1、减少源码的体积2、数据劫持优化3、编译优化(1)、编译粒度的优化 三、语法 API 的优化1、优化了编码的逻辑组织2、优化了代码的逻辑复用…

Java学习系列(四)

1.Scanner类 java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入。 import java.util.Scanner; public class ScannerDemo {public static void main(String[] args) {Scanner scan new Scanner(System.in);// 从键盘接收数据// next…

css学习笔记2

css学习笔记2 CSS三大特性1.三大特性1.1层叠性1.2继承性1.3优先级 2.颜色的表示2.1表示方式一:颜色名2.2表示方式二:rgb或rgba2.3表示方式三:HEX或HEXA2.4表示方式四:HSL或HSLA CSS三大特性 1.三大特性 1.1层叠性 概念&#xff…

SLAM算法与工程实践——SLAM基本库的安装与使用(6):g2o优化库(1)g2o库的安装

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接,本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址: SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…

如何提高React组件的渲染效率的?在React中如何避免不必要的render?

面试官:说说你是如何提高组件的渲染效率的?在React中如何避免不必要的render? 一、是什么 react 基于虚拟 DOM 和高效 Diff 算法的完美配合,实现了对 DOM 最小粒度的更新,大多数情况下,React 对 DOM 的渲染…

debian10安装配置vim+gtags

sudo apt install global gtags --version gtags //生成gtag gtags-cscope //查看gtags gtags与leaderf配合使用 参考: 【VIM】【LeaderF】【Gtags】打造全定制化的IDE开发环境! - 知乎

Apache Superset如何实现无公网ip实时远程访问本地数据【内网穿透】

文章目录 前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网穿透,实现公网访问3. 设置固定连接公网地址 前言 Superset是一款由中国知名科技公司开源的“现代化的…

生物信息学R分析工具包ggkegg的详细使用方法

ggkegg介绍 ggkegg 是一个用于生物信息学研究的工具,可以用于分析和解释基因组学数据,并将其与已知的KEGG数据库进行比较。ggkegg 是从 KEGG 获取信息并使用 ggplot2 和 ggraph 进行解析、分析和可视化的工具包,结合其他使用 KEGG 进行生物功…

HAproxy做七层代理+keepalived高可用,实现动静分离,由nginx处理静态页面,tomcat处理动态页面

目录 一、三种软负载均衡器的区别 关于三种负载均衡器的性能对比: 关于三种负载均衡器的代理类型对比: 关于三种负载均衡器的健康检查对比: 二、haproxy的8中负载均衡调度算法 haproxy的会话保持的方式 haproxy的配置文件学习 三、实操…

Python中导入Excel数据:全面解析与实践

目录 一、引言 二、选择合适的库 三、读取Excel文件 四、处理数据 五、错误处理和异常处理 1、使用try-except语句捕获和处理异常: 2、使用try-except语句捕获和处理特定异常类型: 六、性能优化 七、数据验证 1、检查缺失值: 2、检…

如何解决idea创建版本时只有Java21和Java17选项

idea如果版本高了就会出现在创建Springboot项目时只有Java21和Java17选项 选择jdk1.8的时候很可能出现下图报错,这是因为版本jdk1.8与Java17不兼容 解决办法一般有三种,这里列举两种 1、替换下载数据源 可以将https://start.spring.io/ 替换成 https:…

科普-电子合同签署,这三步不能忽视

关于电子合同,许多人认为我自己直接内部发送邮件/传真等发送电子版合同或者我自己创建一个电子合同平台,这种怎么不属于电子合同呢? 在这里给大家科普一个知识点:签电子合同,需要经过这“三个步骤”。 根据《电子签名…

31. 深度学习进阶 - 全连接层及网络结构

Hi,你好。我是茶桁。 之前的课程咱们学习了卷积以及池化,那到底卷积是如何构成卷积神经网络的呢?我们这节课来好好讲一下。 全连接层 整个卷积的运算就是经过卷积,再经过pooling,再经过卷积。会把这个图形变的很小。…

案例系列:营销模型_客户细分_无监督聚类

案例系列:营销模型_客户细分_无监督聚类 import numpy as np # 线性代数库 import pandas as pd # 数据处理库,CSV文件的输入输出(例如pd.read_csv)/kaggle/input/customer-personality-analysis/marketing_campaign.csv在这个项…

新型智慧视频监控系统:基于TSINGSEE青犀边缘计算AI视频识别技术的应用

边缘计算AI智能识别技术在视频监控领域的应用有很多。这项技术结合了边缘计算和人工智能技术,通过在摄像头或网关设备上运行AI算法,可以在现场实时处理和分析视频数据,从而实现智能识别和分析。目前来说,边缘计算AI视频智能技术可…

Rocky Linux 9.3 安装 Jenkins 2.426.2 (超级详细版本)

安装步骤 官网的安装文档 导入秘钥 sudo wget -O /etc/yum.repos.d/jenkins.repo \https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key 更新yum源 sudo yum upgrade 安装JDK(已…

回顾 2023 这一年的进展,哪些 AI 公司让你觉得未来可期?

文章目录 前言行业趋势1、Open AI 成立于 2015 年2、Tome 成立于 2020 年3、Synthesia 成立于 2017 年4、Uizard 成立于 2018 年5、Soundful 成立于 2019 年6、GoodVision 成立于 2017 年7、Writesonic 成立于 2021 年8、Atomic AI 成立于 2020 年9、Eightfold 成立于 2016 年1…