栏目:网上股票配资开户  作者:767股票学习网  更新:2026-06-05  阅读:13

<怎么买股票>三角形纹理插值:从顶点到内部点的颜色计算

一、理论基础 1、三角形

使用计算机可以绘制出许多非常复杂的图形。但实际上,这些复杂图形都是由平面三角形绘制而成的。因此,三角形的绘制是本文的重点。

三角形有三个顶点(),顶点一般有以下属性:三维坐标(Coord)、颜色(Color)、法线()、纹理坐标(UV)。这些属性,一般都是在模型文件中给出的。

注意,模型文件中不一定有上面给出的所有属性,但三维坐标是必须有的。

这些是顶点的属性,但实际上,三角形中的每一个点都应该有这些属性,三角形其它点的属性应该怎么获得呢?

通过插值算法三角形纹理插值,只需要三角形顶点的属性值,就可以计算出三角形中每一个点的属性值。关于插值算法的内容,我们稍后会介绍。

等等,顶点为什么会有法线?

确实,在数学中,我们所说的法线通常是指平面的法线。但现实中可不仅仅只有平面,如果我们需要绘制一个曲面呢?曲面的每一个点的弯曲程度都不同,因此每一个点都有一个法线。但在图形学中只有一种图形——平面三角形,所以,如果我们想用平面三角形来实现曲面的效果三角形纹理插值,我们就需要为三个顶点设置不同的法线向量,而三角形中其它点的法线,则会通过插值算法计算得来。点的法线是光照理论的基础,关于光照理论的相关内容,我们会在后续章节中学习。

一个顶点可能被多个面共用,每个面都有自己的法线,那这个顶点的法线应该怎么处理?

这并不是我们需要考虑的问题。一般而言,顶点的法线会由共用它的所有面的法线加权平均得来。

纹理坐标是什么?

一个三角形不一定都是纯色的,有时候我们会用一张图来定义这个三角形中每一个点的颜色,这张图被称为纹理。纹理需要贴到三角形上,而纹理坐标就决定了纹理应该怎样贴上去。关于纹理,之后我们还会详细介绍。

2、重心坐标与插值算法

该部分内容学习与否,并不影响后续代码的编写。因此,此小节的内容供对计算机图形学的底层原理有兴趣的读者学习。

此处的插值算法,特指通过重心坐标计算三角形内的点的属性的方法。与计算机图形学中常说的另一类插值算法(最近邻插值、双线性插值等)完全无关。

对于一个三角形ABC,其内的任意一点P的坐标都可以用下面的公式表示:

{P=αA+βB+γCα+β+γ=1\begin{cases} P = \alpha A+ \beta B + \gamma C \\ \\ \alpha + \beta + \gamma = 1 \end{cases}​P=αA+βB+γCα+β+γ=1​

(α,β,γ)(\alpha, \beta, \gamma)(α,β,γ) 即为该点的重心坐标,下面的公式给出了如何计算出α,β,γ\alpha, \beta, \gammaα,β,γ:

在这里插入图片描述

式中的AA,AB,ACA_A, A_B, A_CAA​,AB​,AC​为面积,可以通过向量叉乘的方法计算出面积值。有兴趣的读者可以自行查阅资料,这里不做展开说明。

如果需要计算三角形内一点的某一属性值,则用三个顶点的属性值替换掉原式 P=αA+βB+γCP = \alpha A+ \beta B + \gamma CP=αA+βB+γC 中的A、B、C即可求出(α,β,γ\alpha, \beta, \gammaα,β,γ由四点的坐标计算出)。

3、标准化设备坐标(NDC)

给出一个点的三维坐标后,会通过一系列的处理,将这个三维坐标映射到3^33的空间内(即x、y、z三个维度的值都在-1到1之间)。这个坐标空间,被称为标准化设备坐标。

标准化设备坐标以视口的中心点为原点,向右为x轴正方向,向上为y轴正方向,x轴和 y轴的坐标范围都在-1到1之间。对于x轴而言,视口最左边为-1,最右边为1;对于y轴,视口最下面为-1,最上面为1。

在这里插入图片描述

二、基本图形绘制

三角形顶点属性_三角形纹理插值_重心坐标计算三角形属性

还记得上一章的渲染循环吗?

def loop(self, render):	# 注意loop函数中多了一个render参数
    while not glfw.window_should_close(self.window):
        glClearColor(*self.bgColor)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        
        render()	# 注意这里,相比上一章的代码,这里调用render函数,用于绘制图形
        glfw.swap_buffers(self.window)
        glfw.poll_events()
        if glfw.get_key(self.window, glfw.KEY_ESCAPE) == glfw.PRESS:
            glfw.set_window_should_close(self.window, True)
    glfw.destroy_window(self.window)
    glfw.terminate()

在本文接下来的内容中,如果没有特别说明,文章给出的代码,都写在函数中。

通过和glEnd绘制图形

本文主要介绍通过和glEnd绘制图形,这种方法可以很方便的绘制基本图形。但是,当我们的模型中有大量的三角形时,这种方法就比较繁琐了。所以它并不常用。在下一章中,我们会介绍另一种更常用的绘制三角形的方法。

glBegin(GL_TRIANGLES)
# 第一个顶点
glColor(1, 0, 0)
glVertex(0, 0, 0)
# 第二个顶点
glColor(0, 1, 0)
glVertex(0.5, 0, 0)
# 第三个顶点
glColor(0, 0, 1)
glVertex(0, 0.5, 0)
glEnd()

由定义要绘制的图形的类型。的可使用的参数如下:

类型说明

单个顶点集

多组双顶点线段

单个简单填充凸多边形

多组独立填充三角形

多组独立填充四边形

不闭合折线

闭合折线

线型连续填充三角形串

扇形连续填充三角形串

连续填充四边形串

三角形纹理插值_重心坐标计算三角形属性_三角形顶点属性

使用设置顶点的颜色,使用设置顶点位置(NDC坐标)。

效果图:

在这里插入图片描述

可以看出,三个顶点分别为红色、绿色、蓝色,三角形中的其它点的颜色,则通过插值算法获得。

如果我们稍微调整一下三个顶点的顺序:

glBegin(GL_TRIANGLES)
# 第一个顶点
glColor(0, 0, 1)
glVertex(0, 0.5, 0)
# 第二个顶点
glColor(0, 1, 0)
glVertex(0.5, 0, 0)
# 第三个顶点
glColor(1, 0, 0)
glVertex(0, 0, 0)
glEnd()

此时再运行代码,就会发现三角形不见了!

在这里插入图片描述

这是背面剔除产生的效果。还记得上一章中有这样一句代码吗?

glEnable(GL_CULL_FACE)

这句代码的功能就是开启背面剔除。何为背面剔除?对于一个在空间中的三角形,有时候我们只会看到它的一个面(正面)三角形纹理插值:从顶点到内部点的颜色计算三角形纹理插值:从顶点到内部点的颜色计算,而另一个面(背面)不可见。此时,我们可以不绘制三角形的背面以节省资源,这就是背面剔除。

那么怎么区分三角形的正面和背面呢?采用右手法则。右手四指(除大拇指)按照顶点的书写顺序握拳,大拇指朝外。此时大拇指的方向即为三角形的正面。

如果你学习过向量叉乘,那么如果一个三角形按照ABC的顺序书写,AB × AC的方向即为正面。

如下图,三角形OAB(O为原点)的正面为OC方向。而三角形OBA的正面则为OC的反方向。

在这里插入图片描述

由于使用较少,它的用法就介绍到这里,更详细的内容,请参阅官方文档。

三、微调类

在上一篇文章给出的类中,我们将所有物体的渲染代码都写在loop()方法里。但在实际开发过程中,作为一个工具类,其内部代码不应被修改。因此,我们需要为使用类的用户提供一个接口,以便于其将自己的渲染代码插入到渲染循环中。

具体来说,我们为类添加一个self.列表,并添加一个self.()方法三角形纹理插值:从顶点到内部点的颜色计算,让用户将自己的渲染函数注册到类中,在oop()方法内三角形纹理插值,我们在每一帧都调用self.列表内的所有函数。具体代码如下:

class Window:
    def __init__(self, width, height, title, bgColor=(0.0, 0.0, 0.0, 1.0)):
        #  此处代码与之前相同,故省略
        
        self.render_event = []
	# 用于注册渲染事件的方法
    def add_render_event(self, render_event):
        self.render_event.append(render_event)
	
	# 渲染循环
    def loop(self):
        while not glfw.window_should_close(self.window):
            glClearColor(*self.bgColor)
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
            # 此处调用渲染事件,绘制物体
            for render_event in self.render_event:
                render_event()
            glfw.swap_buffers(self.window)
            glfw.poll_events()
            if glfw.get_key(self.window, glfw.KEY_ESCAPE) == glfw.PRESS:
                glfw.set_window_should_close(self.window, True)
        glfw.destroy_window(self.window)
        glfw.terminate()

封装好类后,使用如下代码即可绘制出三角形。

def render():
    glBegin(GL_TRIANGLES)
    # 第一个顶点
    glColor(1, 0, 0)
    glVertex(0, 0, 0)
    # 第二个顶点
    glColor(0, 1, 0)
    glVertex(0.5, 0, 0)
    # 第三个顶点
    glColor(0, 0, 1)
    glVertex(0, 0.5, 0)
    glEnd()
if __name__ == '__main__':
    w = Window(1920, 1080, "Test")
    w.add_render_event(render)
    w.loop()

四、结语

本文主要介绍了NDC坐标、重心坐标、插值算法,以及如何使用绘制三角形。在下一章中,我们将介绍另一种更常用的绘制三角形的方法,同时简要介绍渲染管线的相应内容。

Tags: