【PyOpenGL教程-介绍着色器】uniform变量(雾)

发表于2018-07-11
评论0 1.9k浏览
uniform变量(雾)

原文地址:http://pyopengl.sourceforge.net/context/tutorials/shader_3.html

本教程基于以往的教程之上新增了:
- 在着色器中定义uniform变量
- 传值给python中的uniform变量
- 在顶点着色器中进行一些基本的计算,包括定义局部变量和使用一些简单的函数
- 通过简单的“雾”效创建“深度提示”,根据顶点与眼睛的距离改变每个顶点的颜色
注:示例中的shader出自OpenGL Shading Language(Orange Book)第九章
现在我们对imports部分已经非常熟悉了……
from OpenGLContext import testingcontext
BaseContext = testingcontext.getInteractive()
from OpenGL.GL import *
from OpenGL.arrays import vbo
from OpenGLContext.arrays import *
from OpenGL.GL import shaders
class TestContext(BaseContext):
    """This shader adds a simple linear fog to shader
    Shows use of uniforms, and a few simple calculations
    within the vertex shader"""
    def OnInit(self):
        """Initialize the context once we have a valid OpenGL environ"""
就像可以用来在顶点和片段着色器之间传递值的”varying”变量一样,”uniform”变量允许我们将值从代码传递到我们的着色器中。你可以将一个uniform值当做一次渲染调用中执行的“uniform”(一样的)的指定值。

这里我们定义两个uniform值:
- end_fog: 相机的雾色将完全遮挡顶点颜色的距离
- fog_color: 将被混合到(顶点)颜色中的雾的颜色
我们还将在主函数中定义2个局部变量,在这种情况下,这些变量是简单的浮点值,但可以是任何受支持的类型。

我们用优化的内置函数ftransform()来取代我们的“第一原理”方法来计算顶点位置,该函数具有一定的性能和可重复性,保证矩阵乘法不一定能提供。与原始操作一样,一旦我们执行了ftransform(),gl_Position就是这个特定顶点的视图空间坐标。

视图空间顶点的“z”坐标代表“屏幕深度”。我们对距离值执行一些基本的数学运算,包括使用我们的end_fog距离的数学运算。然后我们使用生成的浮点值来控制统一的fog_color和当前顶点的gl_Color值的“混合”。
vertex = shaders.compileShader("""
            uniform float end_fog;
            uniform vec4 fog_color;
            void main()
            {
                float fog;// amount of fog to apply
                float fog_coord;// distance for fog calculation
                // ftransfrom() is generally faster and is guaranteed
                // to produce the same result on each run...
                // gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
                gl_Position = ftransform();
                fog_coord = abs(gl_Position.z);
                fog_coord = clamp(fog_coord, 0.0, end_fog);
                fog = (end_fog - fog_coord)/end_fog;
                fog = clamp(fog,0.0,1.0);
                gl_FrontColor = mix(fog_color, gl_Color, fog);
            }""", GL_VERTEX_SHADER)

我们没有定义自定义“varying”变量来传递顶点颜色,而是在顶点着色器中使用了内置(uniform)变化值”gl_FrontColor”。gl_FrontColor的值在我们的片段着色器中出现(使用插值)在类似的内置(legacy)值gl_Color中。

因为我们改变了顶点着色器中每个顶点的颜色,所以雾已经被“烘焙成”了我们得到的插值颜色值。此外,我们可以在片段着色器中完成距离计算,例如可能使用文理查找来提供更真实的雾效,但片段着色器的计算次数要远远超过顶点着色器(一般情况下),这就意味着尽可能地在顶点着色器中进行计算通常是一个好主意。
fragment = shaders.compileShader("""
            void main()
            {
                gl_FragColor = gl_Color;
            }""", GL_FRAGMENT_SHADER)

我们使用与前一教程中相同的代码设置着色器和VBO。
self.shader = shaders.compileProgram(vertex, fragment)
        self.vbo = vbo.VBO(
            array([
                    [ 0, 1, 0, 0,1,0 ],
                    [ -1,-1, 0, 1,1,0 ], 
                    [ 1,-1, 0, 0,1,1 ], 
                    [ 2,-1, 0, 1,0,0], 
                    [ 4,-1, 0, 0,1,0 ], 
                    [ 4, 1, 0, 0,0,1 ], 
                    [ 2,-1, 0, 1,0,0 ], 
                    [ 4, 1, 0, 0,0,1 ],
                    [ 2, 1, 0, 0,1,1 ],
                ],'f')
            )
我们使用“uniform”变量的目的是让我们从(Python)代码中将值传递到我们的着色器中。要做到这一点,我们需要有一种方法来引用来自Python的“uniform”值。GLSL通过“location”提供这些可以从编译的着色器中查询的引用。稍后我们将使用这些不透明的引用将值统一分配给统一值。

glUniform*系列函数允许我们通过提供待传递的值与该值要存储的位置来将值传入uniforms。glUniform既有向量格式也有单个值的格式,这允许我们使用任何存储好数据的数据格式。
self.UNIFORM_LOCATIONS = {
            'end_fog':glGetUniformLocation(self.shader, 'end_fog'),
            'fog_color':glGetUniformLocation(self.shader, 'fog_color'),
        }
    def Render(self, mode):
        """Render the geometry for the scene"""
        BaseContext.Render(self, mode)
        glUseProgram(self.shader)

这里我们指定雾在15个单位远将起到全效。OpenGLContext的默认相机位置是离原点10个单位。我们还将指定雾是白色的,这样几何图形将淡入白色背景中。如果你的背景颜色为黑色的,把雾色设置为黑色,这样最终呈现出来的效果是远处的物体像是淡入雾色似的的效果。
        glUniform1f(self.UNIFORM_LOCATIONS['end_fog'], 15)
        glUniform4f(self.UNIFORM_LOCATIONS['fog_color'],1,1,1,1)

为了使雾效果更有趣,我们将使用一些legacy函数来改变模型-视图矩阵,使几何图形看起来更大,且旋转45度。
        glRotate(45,0,1,0)
        glScale(3,3,3)

正如我们所熟悉的,我们启用VBO,设置我们的顶点和颜色指针并调用我们的绘图函数。
        try:
            self.vbo.bind()
            try:
                glEnableClientState(GL_VERTEX_ARRAY)
                glEnableClientState(GL_COLOR_ARRAY)
                glVertexPointer(3, GL_FLOAT, 24, self.vbo)
                glColorPointer(3, GL_FLOAT, 24, self.vbo+12)
                glDrawArrays(GL_TRIANGLES, 0, 9)
            finally:
                self.vbo.unbind()
                glDisableClientState(GL_VERTEX_ARRAY)
                glDisableClientState(GL_COLOR_ARRAY)
        finally:
            glUseProgram(0)
if __name__ == "__main__":
    TestContext.ContextMainLoop()

你可以使用键盘的“箭头”键在“雾化”的世界里四处走动,随着你离几何远近距离发生变化,你会看到不同的效果。

译者附:
完整代码
from OpenGLContext import testingcontext
BaseContext = testingcontext.getInteractive()
from OpenGL.GL import *
from OpenGL.arrays import vbo
from OpenGLContext.arrays import *
from OpenGL.GL import shaders
class TestContext(BaseContext):
    """This shader adds a simple linear fog to shader
    Shows use of uniforms, and a few simple calculations
    within the vertex shader"""
    def OnInit(self):
        """Initialize the context once we have a valid OpenGL environ"""
        vertex = shaders.compileShader("""
            uniform float end_fog;
            uniform vec4 fog_color;
            void main()
            {
                float fog;// amount of fog to apply
                float fog_coord;// distance for fog calculation
                // ftransfrom() is generally faster and is guaranteed
                // to produce the same result on each run...
                // gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
                gl_Position = ftransform();
                fog_coord = abs(gl_Position.z);
                fog_coord = clamp(fog_coord, 0.0, end_fog);
                fog = (end_fog - fog_coord)/end_fog;
                fog = clamp(fog,0.0,1.0);
                gl_FrontColor = mix(fog_color, gl_Color, fog);
            }""", GL_VERTEX_SHADER)
        fragment = shaders.compileShader("""
            void main()
            {
                gl_FragColor = gl_Color;
            }""", GL_FRAGMENT_SHADER)
        self.shader = shaders.compileProgram(vertex, fragment)
        self.vbo = vbo.VBO(
            array([
                    [ 0, 1, 0, 0,1,0 ],
                    [ -1,-1, 0, 1,1,0 ], 
                    [ 1,-1, 0, 0,1,1 ], 
                    [ 2,-1, 0, 1,0,0], 
                    [ 4,-1, 0, 0,1,0 ], 
                    [ 4, 1, 0, 0,0,1 ], 
                    [ 2,-1, 0, 1,0,0 ], 
                    [ 4, 1, 0, 0,0,1 ],
                    [ 2, 1, 0, 0,1,1 ],
                ],'f')
            )
        self.UNIFORM_LOCATIONS = {
            'end_fog':glGetUniformLocation(self.shader, 'end_fog'),
            'fog_color':glGetUniformLocation(self.shader, 'fog_color'),
        }
    def Render(self, mode):
        """Render the geometry for the scene"""
        BaseContext.Render(self, mode)
        glUseProgram(self.shader)
        glUniform1f(self.UNIFORM_LOCATIONS['end_fog'], 15)
        glUniform4f(self.UNIFORM_LOCATIONS['fog_color'],0,0,0,1)
        glRotate(45,0,1,0)
        glScale(3,3,3)
        try:
            self.vbo.bind()
            try:
                glEnableClientState(GL_VERTEX_ARRAY)
                glEnableClientState(GL_COLOR_ARRAY)
                glVertexPointer(3, GL_FLOAT, 24, self.vbo)
                glColorPointer(3, GL_FLOAT, 24, self.vbo+12)
                glDrawArrays(GL_TRIANGLES, 0, 9)
            finally:
                self.vbo.unbind()
                glDisableClientState(GL_VERTEX_ARRAY)
                glDisableClientState(GL_COLOR_ARRAY)
        finally:
            glUseProgram(0)
if __name__ == "__main__":
    TestContext.ContextMainLoop()
glUniform* 细节
关于glUniform*的更多描述:https://www.khronos.org/opengl/wiki/GLAPI/glUniform
来自:https://blog.csdn.net/v_xchen_v/article/details/80337466

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引