Unity Shader - 兰伯特漫反射

news2024/11/23 0:31:01

兰伯特漫反射公式:

漫反射(Diffuse)= 光源颜色 * max(0,cos(光方向和法线的夹角))

公式原理:

 从上面图片可以看出光照方向 L 与物体法相 N形成的 余弦值越大,反射光越大,角度为0度的时候最强 Cos(0) = 1,大于等于90度的时候为0 Cos(90) = 0;

所以我们首先需要计算出法向量N入射光方向L的角度的余弦值。

我们可以通过他们的点乘来计算,公式如下:

把向量归一化处理后,|L| 和 |N| 都是1,可以简化为:

让我们来实现以下:

逐顶点漫反射:

Shader "Unlit/001"
{
    Properties
    {
        // 漫反射颜色
        _Diffuse ("_Diffuse",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            float4 _Diffuse;
            struct v2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : POSITION;
                float3 color : Color;
            };

            v2f vert (v2v v)
            {
                v2f o;
                // 将对象空间中的点变换到齐次坐标中的摄像机裁剪空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                // 光源方向
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                // 将法向量转到世界坐标法向量
                float3 normal = UnityObjectToWorldNormal(v.normal);
                // 环境光颜色
                float3 ambinet = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 光源颜色 0 表示第一套光源 ,场景里可以有多个光源
                float3 lightColor = _LightColor0.xyz;
                // 漫反射公式 计算
                float3 diffuse = _Diffuse * lightColor * max(0,dot(lightDir,normal));
                o.color = diffuse + ambinet;
                return o;   
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float4 color = float4(i.color,1);
                return color;
            }
            ENDCG
        }
    }
}

逐片源漫反射:

Shader "Unlit/002"
{
    Properties
    {
        // 漫反射颜色
        _Diffuse ("_Diffuse",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            
            float4 _Diffuse;
            struct v2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;                
            };

            v2f vert (v2v v)
            {
                v2f o;
                // 将对象空间中的点变换到齐次坐标中的摄像机裁剪空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                // 将法向量转到世界坐标法向量
                o.normal = UnityObjectToWorldNormal(v.normal);
                return o;   
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 光源方向
                float3 lightDir = _WorldSpaceLightPos0.xyz;
                // 光源颜色
                float lightColor = _LightColor0.rgb;
                // 漫反射公式 计算
                float3 diffuse = lightColor * lightDir * max(0,dot(lightDir,i.normal));
                float3 ambinet = UNITY_LIGHTMODEL_AMBIENT.rgb;
                float3 color = diffuse + ambinet;
                return float4(color,1);
            }
            ENDCG
        }
    }
}

让我们看以下效果:左边是逐顶点漫反射,右边是逐片元漫反射
逐顶点的漫反射与逐片元的漫反射的区别在于顶点漫反射在阴影切换处会有明显的锯齿反应(不过在我这电脑上看并不明显),可以看的到明显的顶点。而片元漫反射则相对切换平滑。相对的,逐片元就比逐顶点好性能。

背光效果:

 我们看到背光的地方非常暗,完全就看不见模型纹理了,这样的话效果在游戏里太影响体验了,于是就有了 半兰伯特光照模型 ,该技术是Valve公式在开发游戏《半条命》时提出的,由于该技术是在原兰伯特光照模型的基础上修改的,所以被称为半兰伯特光照模型 

漫反射(Diffuse)= 光源颜色 * (cos(光方向和法线的夹角)* 0.5 + 0.5)

其实就是把结果范围从 [-1,1] 映射到 [0,1]的范围内。这样的话在背光面也会有明暗变化,不会完全黑掉。

整体代码我就不贴出来了,把公式部分贴出来

// 漫反射公式 计算
//float3 diffuse = _Diffuse * lightColor * max(0,dot(lightDir,normal));
float3 diffuse = _Diffuse * lightColor * (dot(lightDir,normal) * 0.5 + 0.5);

正面:

背面:

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

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

相关文章

力扣笔记(每日随机一题)—— 打折购买糖果的最小开销

问题(简单) 一家商店正在打折销售糖果。每购买 两个 糖果,商店会 免费 送一个糖果。 免费送的糖果唯一的限制是:它的价格需要小于等于购买的两个糖果价格的 较小值 。 比方说,总共有 4 4 4 个糖果,价格…

开源代码分享(3)—微电网鲁棒定价策略(附matlab代码)

1背景介绍 1.1摘要 本论文聚焦于微电网中的能量失衡管理问题,并从电力市场的角度进行研究。与传统电力网不同,微电网可从可再生能源(RES)如太阳能电池板或风力涡轮机等获得额外能源。然而,来自RES的随机输入给平衡供需…

简述Vue的生命周期以及每个阶段做的事情

03_简述Vue的生命周期以及每个阶段做的事情 思路 给出概念 列举出生命周期各个阶段 阐述整体流程 结合实际 扩展:vue3变化 回答范例 每个vue组件实例被创建后都会经过一系列步骤。比如它需要数据观测、模板编译、挂载实例到dom、以及数据变化的时候更新dom、…

Android系统的启动过程(三):Launcher启动过程

Android系统的启动过程(三):Launcher启动过程 摘要&概述 前两篇文章中我们已经将系统启动的过程推进到了系统服务启动完毕之后,本篇文章就来介绍Android系统启动的最后一步:启动Launcher。 这个Launcher我们可以通俗地理解为桌面&#…

深度相机介绍

一、什么是深度相机 (五)深度相机:结构光、TOF、双目相机 - 知乎 传统的RGB彩色普通相机称为2D相机,只能拍摄相机视角内的物体,没有物体到相机的距离信息,只能凭感觉感知物体的远近,没有明确的数…

V90 PN伺服驱动器转矩控制(750报文)

主要介绍通过标准报文加附加报文 750 实现发送驱动报文的控制字、速度给定、转矩限幅及附加转矩给定的功能,首先就是V90在博途环境下的组态,安装GSD文件,GSD文件下载地址如下: https://download.csdn.net/download/m0_46143730/86542047https://download.csdn.net/downloa…

Qt线程的几种使用方法

目录 引言使用方法重写QThread::run()moveToThreadQRunnable使用QtConcurrent使用 完整代码 引言 多线程不应该是一个复杂而令人生畏的东西,它应该只是程序员的一个工具,不应该是调用者过多记忆相关概念,而应该是被调用方应该尽可能的简化调…

Java网络开发(Tomcat)——登陆和注册功能 的 迭代升级 从Jsp到JavaScript + axios + vue 同步到异步

目录 引出前置工作vueaxiosresp0.vue版本的jsp模板1.导包--Json:pom.xml文件:2.新建一个专门用来处理响应的实体类ResData3.在axios中,所有响应必须是 resp.getWriter().write() 的方式,核心代码如下4.在jsp前端代码中导包&#x…

浅谈一级机电管道设计中的压力与介质温度

管道设计是工程设计中的一个非常重要的部分,管道的设计需要考虑到许多因素,其中就包括管道设计压力分类和介质温度分类。这两个因素是在设计管道时必须非常严格考虑的, 首先是管道设计压力分类。在管道设计中,根据工作要求和要传输…

详解 Ansible 自动化运维,提升工作效率

概要 Ansible 是一个模型驱动的配置管理器,支持多节点发布、远程任务执行。默认使用 SSH 进行远程连接。无需在被管理节点上安装附加软件,可使用各种编程语言进行扩展。 一、Ansible基本架构 上图为ansible的基本架构,从上图可以了解到其由以…

算法刷题-关于链表,你该了解这些!

关于链表,你该了解这些! 什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域…

mybatis-plus分页查询(springboot中实现单表和多表查询)

一、mybatis-plus单表查询 使用mybatis-plus实现单表分页查询 非常方便,主要操作步骤如下: 配置分页查询拦截器进行分页查询 1.首先,打开mybatis-plus官网的插件(插件主体) 或者点击mybatis-plus插件 我是配置在s…

KameAI:探索AI驱动的未来,体验聊天GPT与AI绘画的奇妙世界

人工智能的崛起与发展随着科技的飞速发展,人工智能(AI)已经逐渐成为我们生活中不可或缺的一部分。它的出现不仅改变了我们与世界的互动方式,还为各行各业带来巨大的便利。今天,我们就来聊一聊一个类似ChatGPT的人工智能网站—KameAI&#xff…

Nautilus Chain全球行分享会,上海站圆满举办

在北京时间 6 月 9 日,由 Nautilus Chain 主办的“Layer3 模块化区块链的发展探讨”为主题的全球行活动,在上海顺利举办,本次分享会联合主办方还包 括 Stanford Blockchain Accelerator、Zebec Protocol、Tiger VC DAO、Crypto PHD、Rootz L…

Nginx【反向代理负载均衡动静分离】--上

Nginx【反向代理负载均衡动静分离】–上 先看2 个实际需求,引出Nginx 需求1: 访问不同微服务 示意图 需求2: 轮询访问服务 示意图 解决方案: Nginx 反向代理 负载均衡 动静分离 高可用集群 Nginx 在分布式微服务架构的位置 基本介绍 Nginx 是什么? 能干什…

solr快速上手:配置IK中文分词器(七)

0. 引言 solr作为搜索引擎,常用在我们对于搜索速度有较高要求且大数据量的业务场景,我们之前已经配置过英文分词器,但是针对中文分词不够灵活和实用,要实现真正意义上的中文分词,还需要单独安装中文分词器 solr快速上…

【shell 基础13】输入输出与重定向

文章目录 一. 标准输入和标准输出二、重定向1. 定义2. 输出的重定向3. 对标准错误输出重定向4. 输入的重定向 一. 标准输入和标准输出 linux中有三种标准输入输出,分别是STDIN,STDOUT,STDERR,文件描述符分别是 0、1、2。 当运行…

Android Paging3分页+ConcatAdapter+空数据视图+下拉刷新(SwipeRefreshLayout)+加载更多+错误重试 (示例)

文章目录 引入库数据模型定义分页 adapter加载更多 adapter空数据 adapter分页数据源ViewModel 提供加载数据源的方法结合以上实现的 Fragment数据重复问题 引入库 implementation androidx.paging:paging-runtime-ktx:3.1.1paging 库,目前还是有点小bug &#xff…

Java开发技巧-数据结构-使用HashSet判断主键是否存在、使用Pair成对结果返回/Triple三个对象返回

场景 Java中使用HashSet判断主键是否存在 HashSet实现Set接口,由哈希表(实际上是HashMap)实现,但不保证set的迭代顺序,并允许使用null元素。 HashSet的时间复杂度跟HashMap一致,如果没有哈希冲突则时间复…

EXCEL函数笔记1(数学函数、文本函数、日期函数)

数学函数 取整:INT(number) 取余:MOD(number,除数) 四舍五入:ROUND(number,保留几位小数) 取绝对值:ABS(number) 根号处理:SQRT(number) 0到1随机数:RAND(&am…