3D渲染:面法线和顶点法线

news2024/11/24 14:33:36

现在我们回顾了影响对象外观的参数(它们的亮度、颜色等),我们准备开始研究一些简单的着色技术。

在这里插入图片描述

推荐:用 NSDT编辑器 快速搭建可编程3D场景

1、法线

法线在着色中起着核心作用。 大家都知道,如果我们将物体朝向光源,它就会变得更亮。 物体表面的方向对其反射的光量(以及其看起来的亮度)起着重要作用。在物体表面的任一点P的这个方向,可以通过垂直于表面的法线N来表示,如图1所示。
在这里插入图片描述

图 1:注意随着法线方向和光线方向之间的角度增加,球体如何变得更暗。

请注意图 1 中,球体的亮度如何随着光线方向与法线方向之间的角度增大而减小。 这种亮度下降是我们每天都能看到的现象,但可能很少有人知道为什么会发生这种情况。 我们稍后将解释这种现象的原因。 现在,只需要记住:

  • 我们所说的法线(我们用大写字母N表示) 是垂直于表面P点切线的向量。 换句话说,要找到P点的法线,我们需要追踪一条与曲面相切的线,然后取垂直于该切线的向量(请注意,在 3D 中,这将是切平面)。
  • 物体表面上一点的亮度取决于法线方向,法线方向定义了物体表面在该点相对于光的方向。 另一种说法是,物体表面任意给定点的亮度取决于该点法线与光线方向之间的角度。

现在的问题是我们如何计算这个法线? 根据渲染的几何类型,该问题的解决方案的复杂性可能会有很大差异。 球体的法线通常很容易找到。 如果我们知道球体表面上的点的位置和球心,则可以通过球心减去该点的位置来计算该点的法线:

Vec3f N = P - sphereCenter;

如果对象是三角形网格,则每个三角形定义一个平面,并且垂直于该平面的矢量是位于该三角形表面上的任何点的法线。 垂直于三角形平面的向量可以通过该三角形两条边的叉积轻松获得。 请记住 v1xv2 = -v2xv1。 所以边的选择会影响法线的方向。 如果按逆时针顺序声明三角形顶点,则可以使用以下代码:

Vec3f N = (v1-v0).crossProduct(v2-v0);

在这里插入图片描述

图 2:三角形的面法线可以通过该三角形两条边的叉积来计算。
如果三角形位于 xz 平面,则所得法线应为 (0,1,0) 而不是 (0,-1,0),如图 2 所示

以这种方式计算法线就得到了我们所说的面法线(因为整个面的法线是相同的,无论你在该面或三角形上选取的点如何)。 三角形网格的法线也可以定义在三角形的顶点处,在这种情况下,我们将这些法线称为顶点法线。 顶点法线用于一种称为平滑着色的技术,你将在本章末尾找到该技术的描述。 目前,我们只处理面法线。

在程序中如何以及何时计算要着色的点处的表面法线并不重要。 重要且重要的是,当你要着色这一点时,手头有这些信息。 在本节中我们做了一些基本着色的几个程序中,我们在每个几何类中实现了一个名为 getSurfaceProperties() 的特殊方法,其中我们计算了交点处的法线(如果使用光线跟踪)和其他变量 例如我们将在本课后面讨论的纹理坐标。 对于球体和三角形网格几何类型,这些方法的实现如下所示:

class Sphere : public Object 
{ 
    ... 
public: 
    ... 
    void getSurfaceProperties( 
        const Vec3f &hitPoint, 
        const Vec3f &viewDirection, 
        const uint32_t &triIndex, 
        const Vec2f &uv, 
        Vec3f &hitNormal, 
        Vec2f &hitTextureCoordinates) const 
    { 
        hitNormal= Phit - center; 
        hitNormal.normalize(); 
        ... 
    } 
    ... 
}; 
 
class TriangleMesh : public Object 
{ 
    ... 
public: 
    void getSurfaceProperties( 
        const Vec3f &hitPoint, 
        const Vec3f &viewDirection, 
        const uint32_t &triIndex, 
        const Vec2f &uv, 
        Vec3f &hitNormal, 
        Vec2f &hitTextureCoordinates) const 
    { 
        // face normal
        const Vec3f &v0 = P[trisIndex[triIndex * 3]]; 
        const Vec3f &v1 = P[trisIndex[triIndex * 3 + 1]]; 
        const Vec3f &v2 = P[trisIndex[triIndex * 3 + 2]]; 
        hitNormal = (v1 - v0).crossProduct(v2 - v0); 
        hitNormal.normalize(); 
        ... 
    } 
    ... 
}; 

2、简单的着色效果:面比率

现在我们知道如何计算物体表面上的点的法线,我们已经有足够的信息来创建一个简单的着色效果,称为面比率(facing ratio)。 该技术包括计算我们想要着色的点的法线与观察方向的点积。 计算观察方向也非常简单。 当使用光线追踪时,它只是与表面相交处P的光线的相反方向。 如果不使用光线追踪,也可以通过从表面上的P点追踪一条到眼睛的线来简单地找到观察方向:

Vec3f V = (E - P).normalize(); // or -ray.dir if you use ray-tracing

请记住,如果两个向量平行且指向同一方向,则两个向量的点积返回 1;如果两个向量彼此垂直,则点积返回 0。 如果向量指向相反的方向,则点积为负,但如果我们使用该点积的结果作为颜色,那么我们无论如何都不会对负值感兴趣。 如果需要有关点积的介绍,请查看几何课程。 为了避免负面结果,我们需要将结果限制为 0:

float facingRatio = std::max(0, N.dotProduct(V));

在这里插入图片描述

当法线和向量 V 指向同一方向时,点积返回 1。 如果两个向量垂直,则结果为 0。 如果我们使用这种简单的技术对位于框架中间的球体进行着色,那么球体的中心将是白色的,并且当我们远离其中心向边缘移动时,球体将变得更暗,如下所示。
在这里插入图片描述

Vec3f castRay( 
    const Vec3f &orig, const Vec3f &dir, 
    const std::vector<std::unique_ptr<Object>> &objects, 
    const Options &options) 
{ 
    Vec3f hitColor = options.backgroundColor; 
    float tnear = kInfinity; 
    Vec2f uv; 
    uint32_t index = 0; 
    Object *hitObject = nullptr; 
    if (trace(orig, dir, objects, tnear, index, uv, &hitObject)) { 
        Vec3f hitPoint = orig + dir * tnear;  //shaded point 
        Vec3f hitNormal; 
        Vec2f hitTexCoordinates; 
        // compute the normal of the point we want to shade
        hitObject->getSurfaceProperties(hitPoint, dir, index, uv, hitNormal, ...); 
        hitColor = std::max(0.f, hitNormal.dotProduct(-dir));  //facing ratio 
    } 
 
    return hitColor; 
} 

恭喜! 你刚刚了解了第一种着色技术。 现在让我们了解一种更真实的着色方法,该方法将模拟漫反射对象上的光效果。 但在了解这种方法之前,我们首先需要介绍和了解光的概念。

3、平面着色、平滑着色和顶点法线

三角形网格的问题在于它们无法表示完全光滑的表面(除非三角形非常小)。 如果我们希望将刚刚描述的面向比技术应用于多边形网格,我们需要计算与射线相交的三角形的法线,并将面向比计算为该面法线与视图方向之间的点积 。 这种方法的问题在于,它使对象呈现出多面的外观,如下图所示。 因此这种着色方法被称为平面着色
在这里插入图片描述

正如前面课程中多次提到的,通过计算向量 v0v1 和向量 v0v2 的叉积,可以简单地找到三角形的法线,其中 v0、v1 和 v2 代表三角形的顶点。 为了解决这个问题,Henri Gouraud 在 1971 年引入了一种方法,现在称为平滑着色(smooth shading)或 Gouraud 着色。

该技术背后的想法是在多边形网格的表面上产生连续的阴影,即使网格表示的对象并不连续,因为它是由平坦表面的集合(多边形或三角形)构建的。 为此,Gouraud 引入了顶点法线的概念。 这个想法很简单。 我们不是计算或存储面的法线,而是在网格的每个顶点存储法线,其中法线的方向由三角形网格转换自的底层平滑表面确定。 当我们想要计算三角形表面上的点的颜色时,我们可以通过使用命中点重心坐标对在三角形顶点定义的顶点法线进行线性插值来计算“假平滑”法线,而不是使用面法线 。
在这里插入图片描述

该技术如上图所示。 顶点法线在三角形的顶点处定义。 你可以看到它们的方向垂直于构建三角形网格的光滑底层表面。 有时,三角形网格不是直接从平滑表面转换而来,并且必须即时计算顶点法线。 当没有光滑表面来计算顶点法线时,计算顶点法线的不同技术,但我们不会在本课中研究它们。 现在,使用 Maya 或 Blender 等软件来为你完成这项工作,在 Maya 中,你可以选择多边形网格并选择“法线”菜单中的“软化边缘”选项。

事实上,从实用和技术的角度来看,每个三角形都有自己的一组 3 个顶点法线。 这意味着三角形网格的顶点法线总数等于三角形数量乘以 3。在某些情况下,在由 2、3 个或更多三角形共享的顶点上定义的顶点法线是相同的(它们指向 相同的方向),但你可以通过为它们提供不同的方向来实现不同的效果。例如,可以在表面上伪造一些硬边缘。

计算三角形表面上任意点的插值法线的源代码很简单,只要我们知道三角形的顶点法线、三角形上该点的重心坐标以及三角形索引即可。 光栅化和光线追踪都可以为你提供此信息。 顶点法线由你用于创建模型的 3D 程序在模型上生成。 然后将它们导出到几何文件,其中包含三角形的连接信息、顶点位置和三角形的纹理坐标。 然后你需要做的就是结合点重心坐标和三角形顶点法线来计算点插值平滑法线(下面第 17-20 行):

void getSurfaceProperties( 
    const Vec3f &hitPoint, 
    const Vec3f &viewDirection, 
    const uint32_t &triIndex, 
    const Vec2f &uv, 
    Vec3f &hitNormal, 
    Vec2f &hitTextureCoordinates) const 
{ 
    // face normal
    const Vec3f &v0 = P[trisIndex[triIndex * 3]]; 
    const Vec3f &v1 = P[trisIndex[triIndex * 3 + 1]]; 
    const Vec3f &v2 = P[trisIndex[triIndex * 3 + 2]]; 
    hitNormal = (v1 - v0).crossProduct(v2 - v0); 
 
#if 1 
    // compute "smooth" normal using Gouraud's technique (interpolate vertex normals)
    const Vec3f &n0 = N[trisIndex[triIndex * 3]]; 
    const Vec3f &n1 = N[trisIndex[triIndex * 3 + 1]]; 
    const Vec3f &n2 = N[trisIndex[triIndex * 3 + 2]]; 
    hitNormal = (1 - uv.x - uv.y) * n0 + uv.x * n1 + uv.y * n2; 
#endif 
 
    // doesn't need to be normalized as the N's are normalized but just for safety
    hitNormal.normalize(); 
 
    // texture coordinates
    const Vec2f &st0 = texCoordinates[trisIndex[triIndex * 3]]; 
    const Vec2f &st1 = texCoordinates[trisIndex[triIndex * 3 + 1]]; 
    const Vec2f &st2 = texCoordinates[trisIndex[triIndex * 3 + 2]]; 
    hitTextureCoordinates = (1 - uv.x - uv.y) * st0 + uv.x * st1 + uv.y * st2; 
} 

请注意,这只会产生表面光滑的印象。 如果你查看下图中的多边形球体,仍然可以看到轮廓是多面的,即使内部表面看起来很光滑。 该技术改善了三角形网格的外观,但当然并不能完全解决其多面外观的问题。 该问题的唯一解决方案是使用细分曲面(我们将在不同的部分中讨论),或者当然增加将平滑曲面转换为三角形网格时使用的三角形数量。

在这里插入图片描述

我们还没有准备好学习如何重现漫反射表面的外观。 尽管漫射表面需要光线才能可见。 因此,在研究这项技术之前,我们首先需要了解如何处理 3D 引擎中的光源概念。


原文链接:面法线和顶点法线 — BimAnt

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

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

相关文章

Unity ShaderGraph教程——进阶shader(水面、积雪,数字线框)

1.水面&#xff08;一&#xff09; 公式&#xff1a;场景深度 节点深度 — 屏幕空间位置的W向量 半透明物体与不透明物体的相交边缘 原理&#xff1a;场景深度 节点深度包含透明像素&#xff0c;屏幕空间w向量不包含透明像素。 注意&#xff1a;需要在UniversalRP-xxxQuali…

diskqueue怎么写入消息,怎么对外发送消息

nsq中diskqueue是nsq消息持久化的核心&#xff0c;内容较多&#xff0c;一共分为多篇 1. diskqueue是什么&#xff0c;为什么需要它&#xff0c;整体架构图&#xff0c;对外接口_YZF_Kevin的博客-CSDN博客 2. diskqueue的元数据文件&#xff0c;数据文件&#xff0c;启动入口…

成都睿趣科技:现在开一家抖音小店还来得及吗

随着社交媒体的迅猛发展&#xff0c;抖音已经成为了一个全球范围内广受欢迎的社交平台。在这个短视频应用上&#xff0c;人们分享着各种各样的内容&#xff0c;从搞笑段子到美食教程&#xff0c;再到时尚搭配和手工艺品制作。随着用户数量的不断增长&#xff0c;很多人都在思考…

Python第三方库 - matplotlib库

1 matplotlib了解 Matplotlib 可能是 Python 2D - 绘图领域使用最广泛的套件。它能让使用者很轻松地将数据图形化&#xff0c;并且提供多样化的输出格式。这里将会探索 matplotlib 的常见用法。 2 matplotlib学习 2.1 引用 plt 表示当前子图&#xff0c;若没有就创建一个子图 …

x64dbg的安装

一、安装地址&#xff1a; 地址 解压目录 点击x96dbf.exe 二、使用 1.反汇编窗口 这个位置显示的是需要分析的程序的反汇编代码。在第一个区域的最左侧例如“7712EAA3”这一列就是内存地址区域&#xff0c;接着“E8 07”就是汇编指令的opcode&#xff0c;“jmp xxxxxxxxx”这…

天津Java培训机构 Java的发展空间如何?

在当今互联网时代&#xff0c;计算机技术的发展日新月异&#xff0c;越来越多人看到IT行业的广泛前景&#xff0c;纷纷想要转行成为一名程序员&#xff0c;作为一名IT从业人员&#xff0c;学习一门编程语言是必不可少的&#xff0c;而在众多编程语言中&#xff0c;Java无疑是较…

存储数据恢复- raid5多块硬盘出现坏道的数据恢复案例

存储数据恢复环境&#xff1a; 某单位一台存储&#xff0c;1个机头4个扩展柜&#xff0c;有两组分别由27块和23块硬盘组建的RAID5阵列。其中由27块磁盘组建的那一组RAID5阵列崩溃&#xff0c;这组RAID5阵列存放是Oracle数据库文件。存储系统上层共划分了11个卷。 存储故障&…

【工具】Linux下常用录屏软件

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…

深圳企业宣传片怎么做

要拍摄企业宣传片&#xff0c;首先要搞清楚客户宣传片的目的和用途&#xff0c;然后根据自身情况拟定预算以及制作周期&#xff0c;再与甲方沟通具体需求&#xff0c;最后进入制作流程。整体制作流程可以分为以下步骤&#xff0c;由深圳企业宣传片制作公司老友记小编为您解答&a…

LeetCode--HOT100题(48)

目录 题目描述&#xff1a;437. 路径总和 III&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;437. 路径总和 III&#xff08;中等&#xff09; 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和…

Python数据分析实战-将字符串中的空格替换为逗号且要保留特定词组(附源码和实现效果)

实现功能 将字符串中的空格替换为逗号且要保留特定词组 实现代码 import restring "Linux Python Cloud Native Distributed System AI C Deep Learning Framework Micro Service Automation Git IoT"# 定义要保留的特定词组 special_phrases ["Deep Learn…

新手可以选黄金代理吗?

我们都知道选择现货黄金平台的时候&#xff0c;一定要选择一个正规的、合法的平台&#xff0c;这样投资者才可以安心进行交易&#xff0c;但是目前市面上我们看到很多的是黄金代理&#xff0c;而不是直接与现货黄金平台发生接触&#xff0c;那么&#xff0c;这种黄金代理在市场…

如何根据需求正确选择适合企业的CRM销售管理系统

现代企业的销售工作离不开使用各种各样的销售管理系统&#xff0c;随着互联网的发展&#xff0c;市面上出现了许多销售管理系统&#xff0c;那么销售管理系统哪种好呢&#xff1f;如何选择一款适合自己企业的CRM销售管理系呢&#xff1f;本文将从多个角度进行分析和比较为大家提…

nc前端合计行

nc前端合计行 1.无表体和单表体的合计行加法 只要卡片下 如果是只有表头要合计行就只留ShowTotalLine&#xff1b;如果是只有表体要合计行就只留ShowTotalLineTabcodes 2.多表体的合计行加法 表头卡片下和列表下都要 3.档案的合计行加法 重写一下列表模板

C++(18):异常处理

异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。 异常使得能够将问题的检测与解决过程分离开来&#xff1a;程序的一部分负责检测问题的出现&#xff0c;然后解决该问题的任务传递给程序的另一部分。检测环节无须知道问题处理模块的…

基于 Web HID API 的HID透传测试工具(纯前端)

前言 最近再搞HID透传 《STM32 USB使用记录&#xff1a;HID类设备&#xff08;后篇&#xff09;》 。 市面上的各种测试工具都或多或少存在问题&#xff0c;所以就自己写一个工具进行测试。目前来说纯前端方案编写这个工具应该是最方便的&#xff0c;这里放上相关代码。 项目…

通过idea实现springboot集成mybatys

概述 使用springboot 集成 mybatys后&#xff0c;通过http请求接口&#xff0c;使得通过http请求可以直接直接操作数据库&#xff1b; 完成后端功能框架&#xff1b;前端是准备上小程序&#xff0c;调用https的请求接口用。简单实现后端框架&#xff1b; 详细 springboot 集…

qt中子窗口最小化后再恢复显示窗口区域显示为全白色

问题&#xff1a; qt中子窗口最小化后再恢复显示窗口区域显示为全白色&#xff0c;如下图&#xff1a; 原因&#xff1a; 恢复显示后窗口为及时刷新。 解决办法&#xff1a; 重写showEvent函数&#xff0c;如下&#xff1a; void MyClass::showEvent(QShowEvent *event) {se…

OS | 第5章 插叙:进程API

OS | 第5章 插叙&#xff1a;进程API 文章目录 OS | 第5章 插叙&#xff1a;进程API5.1 fork()系统调用代码过程分析 5.2 wait()系统调用5.3 exec() 系统调用执行过程 为什么这样设计API&#xff1f;shell执行过程shell 重定向pipe()>>>>> 欢迎关注公众号【三戒…

YOLOv5:解读metrics.py

YOLOv5&#xff1a;解读metrics.py 前言前提条件相关介绍metrics.pyfitnesssmoothbox_iouConfusionMatrix ★ ★ \bigstar\bigstar ★★bbox_iou ★ ★ \bigstar\bigstar ★★compute_apap_per_class&#xff08;难度&#xff1a; ⋆ ⋆ ⋆ ⋆ ⋆ \star\star\star\star\star ⋆…