PyOpenGL:渲染……好吧……几乎任何东西
我最近在用Python和OpenGL做一个项目,已经有一段时间了。我之前发过一个类似的问题,但现在我做了一些研究,换用了不再被淘汰的函数。根据这个教程(当然是把它翻译成Python版本),我写出了以下代码:
import sys
import OpenGL
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GLUT.freeglut import *
from OpenGL.arrays import vbo
import pygame
import Image
import numpy
class AClass:
def __init__(self):
self.Splash = True #There's actually more here, but it's impertinent
def TexFromPNG(self, filename):
img = Image.open(filename) # .jpg, .bmp, etc. also work
img_data = numpy.array(list(img.getdata()), 'B')
texture = glGenTextures(1)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
glBindTexture(GL_TEXTURE_2D, texture)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size[0], img.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
return texture
def MakeBuffer(self, target, data, size):
TempBuffer = glGenBuffers(1)
glBindBuffer(target, TempBuffer)
glBufferData(target, size, data, GL_STATIC_DRAW)
return TempBuffer
def ReadFile(self, filename):
tempfile = open(filename,'r')
source = tempfile.read()
temprfile.close()
return source
def run(self):
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(256,244)
self.window = glutCreateWindow("test")
glutReshapeFunc(self.reshape)
glutDisplayFunc(self.draw)
glutKeyboardFunc(self.keypress)
self.MainTex = glGenTextures(1)
self.SplashTex = self.TexFromPNG("Resources/Splash.png")
MainVertexData = numpy.array([-1,-1,1,-1,-1,1,1,1],numpy.float32)
FullWindowVertices = numpy.array([0,1,2,3],numpy.ushort)
self.MainVertexData = self.MakeBuffer(GL_ARRAY_BUFFER,MainVertexData,len(MainVertexData))
self.FullWindowVertices = self.MakeBuffer(GL_ELEMENT_ARRAY_BUFFER,FullWindowVertices,len(FullWindowVertices))
self.BaseProgram = compileProgram(compileShader(self.ReadFile("Shaders/Mainv.glsl"),
GL_VERTEX_SHADER),
compileShader(self.ReadFile("Shaders/Mainf.glsl"),
GL_FRAGMENT_SHADER))
glutMainLoop()
def reshape(self, width, height):
self.width = width
self.height = height
glutPostRedisplay()
def draw(self):
glViewport(0, 0, self.width, self.height)
glClearDepth(1)
glClearColor(0,0,0,0)
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_TEXTURE_2D)
if self.Splash:
glUseProgram(self.BaseProgram)
pos = glGetAttribLocation(self.BaseProgram, "position")
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.SplashTex)
glUniform1i(glGetUniformLocation(self.BaseProgram,"texture"), 0)
glBindBuffer(GL_ARRAY_BUFFER,self.MainVertexData)
glVertexAttribPointer(pos,
2,
GL_FLOAT,
GL_FALSE,
0,
numpy.array([0],numpy.uint8))
glEnableVertexAttribArray(pos)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,self.FullWindowVertices)
glDrawElements(GL_TRIANGLE_STRIP,
4,
GL_UNSIGNED_SHORT,
numpy.array([0],numpy.uint8))
glDisableVertexAttribArray(pos)
else:
glBindTexture(GL_TEXTURE_2D, self.MainTex)
glutSwapBuffers()
glutInit(sys.argv)
test = AClass()
test.run()
Shaders/Mainv.glsl和Shaders/Mainf.glsl文件里包含:
#version 110
attribute vec2 position;
varying vec2 texcoord;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
texcoord = position * vec2(0.5) + vec2(0.5);
}
还有:
#version 110
uniform sampler2D texture;
varying vec2 texcoord;
void main()
{
gl_FragColor = texture2D(texture, texcoord);
}
分别是这些内容。
这段代码让我得到了一个(清晰颜色的)GLUT窗口,但似乎没有渲染我的三角形,不知道为什么会这样。我找不到任何不使用被淘汰函数的PyOpenGL的例子,所以我也不知道他们和我有什么不同,可能我漏掉了什么。
2 个回答
5
我觉得这对刚开始学习PyOpenGL和可编程管线的人来说是个很有用的参考,所以我根据Bethor和Josiah的评论修正了代码,并且稍微简化了一下(把着色器作为字符串嵌入)。这段代码对我来说是有效的。它假设你在同一个文件夹里有test.png这个文件。
import sys import OpenGL from OpenGL.GL import * from OpenGL.GL.shaders import * from OpenGL.GLU import * from OpenGL.GLUT import * from OpenGL.GLUT.freeglut import * from OpenGL.arrays import vbo import Image import numpy # vertex shader strVS = """ attribute vec2 position; varying vec2 texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); texcoord = position * vec2(0.5) + vec2(0.5); } """ # fragment shader strFS = """ uniform sampler2D texture; varying vec2 texcoord; void main() { gl_FragColor = texture2D(texture, texcoord); } """ class AClass: def __init__(self): self.Splash = True #There's actually more here, but it's impertinent def TexFromPNG(self, filename): img = Image.open(filename) # .jpg, .bmp, etc. also work img_data = numpy.array(list(img.getdata()), 'B') texture = glGenTextures(1) glPixelStorei(GL_UNPACK_ALIGNMENT,1) glBindTexture(GL_TEXTURE_2D, texture) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size[0], img.size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data) return texture def MakeBuffer(self, target, data, size): TempBuffer = glGenBuffers(1) glBindBuffer(target, TempBuffer) glBufferData(target, size, data, GL_STATIC_DRAW) return TempBuffer def run(self): glutInitDisplayMode(GLUT_RGBA) glutInitWindowSize(256,244) self.window = glutCreateWindow("test") glutReshapeFunc(self.reshape) glutDisplayFunc(self.draw) self.MainTex = glGenTextures(1) self.SplashTex = self.TexFromPNG("test.png") MainVertexData = numpy.array([-1,-1,1,-1,-1,1,1,1],numpy.float32) FullWindowVertices = numpy.array([0,1,2,3],numpy.ushort) self.MainVertexData = self.MakeBuffer(GL_ARRAY_BUFFER,MainVertexData,4*len(MainVertexData)) self.FullWindowVertices = self.MakeBuffer(GL_ELEMENT_ARRAY_BUFFER,FullWindowVertices,2*len(FullWindowVertices)) self.BaseProgram = compileProgram(compileShader(strVS, GL_VERTEX_SHADER), compileShader(strFS, GL_FRAGMENT_SHADER)) glutMainLoop() def reshape(self, width, height): self.width = width self.height = height glutPostRedisplay() def draw(self): glViewport(0, 0, self.width, self.height) glClearDepth(1) glClearColor(0,0,0,0) glClear(GL_COLOR_BUFFER_BIT) glEnable(GL_TEXTURE_2D) if self.Splash: glUseProgram(self.BaseProgram) pos = glGetAttribLocation(self.BaseProgram, "position") glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.SplashTex) glUniform1i(glGetUniformLocation(self.BaseProgram,"texture"), 0) glBindBuffer(GL_ARRAY_BUFFER,self.MainVertexData) glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, None) glEnableVertexAttribArray(pos) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,self.FullWindowVertices) glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, None) glDisableVertexAttribArray(pos) else: glBindTexture(GL_TEXTURE_2D, self.MainTex) glutSwapBuffers() glutInit(sys.argv) test = AClass() test.run()
这里是输出结果:
3
我成功让你的代码运行起来了,修复了以下几个问题:
- glBufferData函数需要传入数据的字节大小,而不是元素的数量:对于顶点数据(浮点数),你需要乘以4;对于索引数据(短整型),需要乘以2。
- 如果你使用了VBO(顶点缓冲对象),那么glVertexAttribPointer的最后一个参数是指向当前绑定的ARRAY_BUFFER的偏移量;在你的情况下,这个值应该是空指针,也就是在使用pyOpenGL时填None。
- glDrawElements的行为也是一样:最后一个参数是指向ELEMENT_ARRAY_BUFFER的偏移量,在这种情况下也应该是None。
修复这些问题后,我的测试图像显示出来了,虽然颜色有点乱。我没有去调试这个问题,因为你用来解码png文件的选项可能是针对你特定数据的 :)