使用OpenGL风扇的好处是什么,与基于不同位置创建圆形相比?

3

我看过的所有关于在OpenGL中绘制圆形的教程都使用了“扇形”方法(或其变体),但我没有看到有人使用以下方法:

创建如下所示的一组顶点:

float vertices[] = {
            0.5f,  0.5f, 0.0f,  1.0f,  1.0f, // top right
            0.5f, -0.5f, 0.0f,  1.0f, -1.0f, // bottom right
            -0.5f, -0.5f, 0.0f, -1.0f, -1.0f,// bottom left
            -0.5f,  0.5f, 0.0f,  -1.0f,  1.0f// top left
};

创建以下顶点着色器,以可变格式将边界信息传递给片段着色器:
#version 330 core

in vec3 apos;
in vec2 abound;

out vec3 ourColor;
out vec2 ourBound;

void main() {
    gl_Position = vec4(apos, 1.0);
    ourBound = abound;
}

创建以下片段着色器,它使用插值的边界值来计算当前片段距离中心点的距离。
#version 330 core

in vec2 ourBound;

uniform vec4 setColor;

void main() {
    float dist = sqrt((ourBound.x * ourBound.x) + (ourBound.y * ourBound.y));
    vec4 color_used = vec4(vec3(1.0), 0.0);
    if(dist < 1.0){
        color_used = setColor;
    }
    gl_FragColor = color_used;
}
  • 加载着色器
  • 启用混合
  • 为apos(偏移量=0,步幅=5 * sizeof(float))和abound(偏移量=3,步幅=5 * sizeof(float))初始化VBO / EBO对象
  • 初始化colorLocation
  • 存储在VBA中

在绘制循环中加载程序:

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(shader_program);
    glUniform4f(colorLocation, 0.0f, 1.0f, 0.0f, 1.0f);
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

我的结果 输入图像描述

我不明白为什么要创建一个风扇,这样做有什么性能优势吗?看起来肯定不是更简单了。除了这里提供的方法,我在谷歌上找不到其他替代方案。


如果您无法访问可编程管道并且无法进行每像素的incircle()检查,会发生什么? - genpfault
@genpfault 你是什么意思?在什么情况下,这种情况仍符合非古老的OpenGL标准,特别是3.3+核心?这将意味着您甚至无法制作着色器。无论如何,这个风扇图案似乎都会被教授,不管OpenGL版本如何。 - Krupip
如果你没有着色器,就无法使用incircle()技巧。三角形扇形方法适用于固定函数和可编程管线。 - genpfault
@genpfault 这不是“诡计”……你会用什么设备制作着色器,却不能使用varying变量呢?此外,也没有“incircle()”函数,我原以为你只是在提到之前的if语句(可以轻松转换为无分支的clamp),但你的措辞似乎暗示了其他意思。 - Krupip
2个回答

6
我不明白为什么要创建一个扇形。
这是一篇教程。教程的目的是教授特定的技能,而不一定是呈现最佳实践。在教程中重要的不是结果,而是你达成结果的方式。
创建圆形的两种方法需要完全不同的技能。顶点法涉及使用sin/cos计算圆边缘上的点,以及如何将这些数据上传到缓冲区并用该缓冲区进行渲染。你的伪装法演示了如何创建二维伪装,并如何判断一个点是否在(单位)圆内。
知道如何进行点-圆关系的人不知道如何使用sin/cos计算圆上的点。因此,如果你接下来要做球体,就必须教他们有关sin/cos的知识。而如果你之前通过顶点做过圆,则他们已经领先一步,可以学习作为他们已经掌握的知识的扩展的球面坐标系。
此外,顶点法是教用户更多的顶点可以创建更平滑的结果的简单方法。它还帮助他们学习这是分辨率相关的;离基于顶点的圆越远,顶点数量就越不重要。你甚至可以使用这个来教人们如何做动态LOD,其中你每帧重新计算顶点。这涉及到动态地将数据流传输到缓冲区对象。
对于仅仅的圆来说,使用动态LOD可能并不重要。重要的是用户已经学会了如何进行缓冲区流。
这是两组不同的技能。这两组都是有意义和有用的,但它们是不同的。没有任何本质上的错误方法;关键在于你想教授哪些技能。
“显然并不简单。”这取决于你如何定义“简单”。如果教程试图教用户理解三角形渲染,则你的伪装法并不简单。它也没有教授三角形渲染。
你的方法需要用户在片段级别有正式的理解。不要误解我;那是一个好东西。教用户如何制作伪装是一个非常好的工具。但是,用户是否准备在他们的发展过程中学习这个工具?
此外,着色器方法更为有限。假设你想向这个圆添加纹理。使用顶点法,你做正常的事情:将纹理坐标添加到顶点中,将它们传递到片段着色器,并让它从纹理中提取。使用着色器方法,FS必须发明从纹理到圆的特定映射。
这意味着每当您想要更改此映射时,您必须更改您的FS。您可以向FS添加一些参数以支持缩放映射的变化,但即使如此,这也意味着您现在必须打破三角形批次以更改映射参数。而采用顶点方法,您可以通过将映射烘焙到顶点数据中,在单个绘制调用中呈现任意数量的风扇(感谢顶点重启)。
事实上,采用顶点方法,您可以使用完全相同的着色器来渲染您的圆形,就像渲染任何其他形状一样。而着色器方法需要改变着色器才能渲染不同的东西。
个人认为教程应该比现在更早地教授冒牌者方法。但我不认为圆形冒牌者是最好的选择。

有一些性能优势吗?

嗯,如果从抗锯齿的角度来看,确实有性能优势。您的基于片段的方法需要特殊编码才能与多重采样配合使用。为了做到正确,您需要计算每个采样位置的距离,然后输出与之对应的样本掩码。或者调用每个样本的着色。无论哪种方法,这都会慢一些。
但这将是一个有趣的课程。

多重采样的想法也很有道理,我没有想到过(我来自CUDA / OpenCL背景,OpenGL让我感到沮丧,因为它与我所知道的GPU能够做的相比非常受限制)。如果我有适当的计算工具,这不会是一个问题,但在OpenGL中处理这个问题并不容易。 - Krupip
@snb:什么不会成为问题?多重采样是三角形光栅化的副产品;你不能直接使用“适当的计算实用程序”来调用它。片段着色器为您提供了设置采样掩码所需的所有工具;只是这不会是免费的,因为您仍然需要为每个采样进行计算。 - Nicol Bolas
@snb:“我来自CUDA/OpenCL背景。”这实际上使得顶点方法对你更加重要。你的本能反应会是“在着色器中解决它”,因为那是你最擅长的。如果你想学习图形学,你需要放下这样的本能。是的,图形管线包括着色器,但它也包括更多,一个好的图形程序员需要知道如何使用所有可用的工具。因为即使伪装者在这种情况下是更好的解决方案,这也不意味着它通常是正确的答案。 - Nicol Bolas
@snb:我已经添加了顶点方法的几个更有用的特性。 - Nicol Bolas
@snb:"如果你想要的话,你可以在CUDA中实现OpenGL本身。"但只有当你想让渲染变得非常慢的时候。CUDA无法访问硬件光栅化器或图形处理管道的许多其他方面。这意味着没有基于硬件的Hi-Z深度剔除等等。此外,GLSL为您提供了“与像素屏幕坐标实际对应的位置”;它被称为gl_FragCoord - Nicol Bolas
我实在看不出有人能够在这个阶段给出更好的答案,所以我接受了这个答案。 - Krupip

0
这与性能有关吗?在您的版本中,顶点扇形可能更好,但如果按以下方式更改片段着色器,则应该同样快。
#version 330 core

in vec2 ourBound;

uniform vec4 setColor;

void main() {
    //no sqrt required
    float dist = dot(ourBound,ourBound);

    //discard pixels on the outside.
    //We can leave blending disabled.
    if(dist > 1.0){
        discard;
    }
    vec4 color_used = vec4(vec3(1.0), 0.0);
    gl_FragColor = color_used;
}

我不明白为什么要创建一个风扇会有什么好处。
如果你只想显示一个二维圆形,我认为没有任何好处。
另外一种方法:
由于你似乎只想显示一个简单的二维圆形,你可以渲染四边形,并在其上放置具有阿尔法通道的贴图。这样可以轻松得到一个非常漂亮的抗锯齿圆或任何其他的二维对象。

纹理方法的问题在于它无法进行缩放,如果我改变我的四边形的位置/比例,我的圆仍然保持其尺寸(前提是四边形仍然是正方形,这也意味着当它不是正方形时,容易出现椭圆形),并且不会像纹理一样丢失任何信息或产生任何混叠伪影。 - Krupip
还有,谢谢你提供关于 dot 的信息,我完全没有想到 glsl 会有这个函数。 - Krupip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接