原文地址:http://pyopengl.sourceforge.net/context/tutorials/shader_2.html
varying变量(颜色)
【译】【PyOpenGL教程-介绍着色器】 varying变量(颜色)
本教程基于以往的教程之上新增了:
- 使用varying变量在顶点着色器和片段着色器之间进行通信
- 捕捉着色器中的编译错误
- 将顶点和颜色值打包到一个VBO中
- 启用带有跨步值的顶点数组
- 启用颜色数组(传统方法)
我们为本教程import的内容和上一篇教程几乎相同,因此我们可以忽略它们。如果您不认识某些东西,请返回上一教程的介绍。
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 just passes gl_Color from an input array to
the fragment shader , which interpolates the values across the
face(via a"varying" data type"""
def OnInit(self):
"""Initialize the context once we have a valid OpenGL environ"""
除此之外:编译错误
随着我们获得越来越复杂的着色器,您将不可避免地遇到着色器出现编译错误并需要调试的情况。当着色器编译失败时,着色器的PyOpenGL便利包会引发RuntimeError实例。RuntimeError的第二个参数是发生在故障时正在编译的源代码。通常,这个错误的Python追溯足以帮助你追踪问题(当然有适当的参考)
try:
shaders.compileShader("""void main(){""",GL_VERTEX_SHADER)
except(GLError, RuntimeError) as err:
print("Example of shader compile error", err)
else:
raise RuntimeError("""Didn't catch compilation error!""")
Varying变量
在我们之前的教程中,我们将每个片元的颜色值算为常量颜色(绿色)。现在我们将为每个顶点赋予不同的颜色值,并让GL对这些颜色之间进行插值。
我们将为顶点使用传统的OpenGL颜色,也就是通常提供给传统(固定功能)管线的颜色。该值显示为内置的vec4 “gl_Color”。在顶点着色器中,顶点着色器每次调用都将分配gl_Color。
为了将每个顶点的颜色传递给片段着色器,我们需要定义一个”varying”变量。对每个片元在三角形内插变化变量,对构成三角形的每个角的顶点采用视角正确的混合值。因此,如果我们将一个顶点定义为黑色,而另一个顶点定义为白色,那么为它们之间的区域生成的碎片将会从黑色变成白色(通过灰色)
你会注意到我们在主函数的外部定义了变化的值,可以粗略地将变化的值视为“全局”,以便可以在两个着色器之间看到它。但是,变化的值正在通过中间的插值处理。
vertex = shaders.compileShader(
"""
varying vec4 vertex_color;
void main(){
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vertex_color = gl_Color;
}""", GL_VERTEX_SHADER)
我们的片段着色器再次声明了vertex_color变化值。由于我们希望最终的片段着色器是顶点之间的插值颜色,我们可以简单地将vertex_color分配给gl_FragColor.
fragment = shaders.compileShader(
"""
varying vec4 vertex_color;
void main(){
gl_FragColor = vertex_color;
}""", GL_FRAGMENT_SHADER)
现在我们的几何体对于每个顶点都有两个分量,第一个是顶点位置,它与我们前面的教程中看到的是相同的一组值。每个顶点(行)中的前三个浮点就是位置,后三个值表示每个顶点的颜色。因此三角形(前三个顶点),将从红色到黄色混合为青色。
正如前面教程中所指出的那样,这种“打包”格式在现代硬件上往往比为每种类型的数据提供单独的数据阵列效率更高。
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')
)
def Render(self, mode):
"""Render the geometry for the scene"""
BaseContext.Render(self, mode)
与前面一样,我们需要启用我们编译的着色器,并使VBO处于活动状态,以便数组规范例程将使用VBO作为我们几何数据的源。
glUseProgram(self.shader)
try:
self.vbo.bind()
try:
既然我们想为着色器提供位置和颜色数组,我们需要启用两个不同的数组。这两个内置数组映射到我们在顶点着色器中使用的内置gl_Vertex和gl_Color“属性”变量。
这些“启用”告诉OpenGL,对于我们渲染的两个顶点,我们希望从启用的阵列中读取一条记录。如果我们要在不指定数组的情况下执行此操作,则OpenGL可能会在尝试访问NULL内存位置时对车观念许进行分段错误。
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
我们在这里使用数组定义调用的“完整”形式,因为我们希望能够在数据数组中“指定”跨度。指针定义调用的参数是:
- 大小:每条记录中值的数量
- 类型:常量定义记录中每个项目的值的类型
- 跨度:每个连续记录开始之间的字节数,在我们的例子中,每条纪律有6个32位浮点值,记录之间共有4*6 = 24个字 节。
- 指针:指向我们希望用于此数组的数据
顶点指针被传递给我们的VBO的引用,它告诉OpenGL从当前绑定的VBO中读取数据。实质上,VBO包装只是将一个空指针传递给GL。
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 just passes gl_Color from an input array to
the fragment shader , which interpolates the values across the
face(via a"varying" data type"""
def OnInit(self):
"""Initialize the context once we have a valid OpenGL environ"""
# try:
# shaders.compileShader("""void main(){""",GL_VERTEX_SHADER)
# except(GLError, RuntimeError) as err:
# print("Example of shader compile error", err)
# else:
# raise RuntimeError("""Didn't catch compilation error!""")
vertex = shaders.compileShader(
"""
varying vec4 vertex_color;
void main(){
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vertex_color = gl_Color;
}""", GL_VERTEX_SHADER)
fragment = shaders.compileShader(
"""
varying vec4 vertex_color;
void main(){
gl_FragColor = vertex_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')
)
def Render(self, mode):
"""Render the geometry for the scene"""
BaseContext.Render(self, mode)
glUseProgram(self.shader)
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()