mor*_*sen -1 python image pyopengl wavefront
我是 PYopenGL 的新手,实际上,我也不确定 PYopenGL 是否适合我的任务。
我有一个 Wavefront obj 文件格式的 3D 模型。我需要从给定的视图中获取模型的“打印屏幕”。换句话说,我需要渲染模型而不是显示模型将其保存为图像(jpg)
我的想法是使用 PYopenGL 来完成这项任务。但是,谷歌搜索我找不到如何做到这一点的建议或示例。因此,我开始怀疑 PYopenGL 是否适合我的任务。
你们中有人已经有了这样的东西,或者知道一个我可以用来学习的例子吗?
提前致谢。
道
GLUT 隐藏窗口方法更简单,并且与平台无关,但会导致窗口闪烁。
要为 Django ie 设置它,您可以将渲染器实现为单独的 Web 服务器,它只会在启动时按窗口闪烁一次,然后通过 http 响应返回渲染图像。
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
from PIL import ImageOps
import sys
width, height = 300, 300
def init():
glClearColor(0.5, 0.5, 0.5, 1.0)
glColor(0.0, 1.0, 0.0)
gluOrtho2D(-1.0, 1.0, -1.0, 1.0)
glViewport(0, 0, width, height)
def render():
glClear(GL_COLOR_BUFFER_BIT)
# draw xy axis with arrows
glBegin(GL_LINES)
# x
glVertex2d(-1, 0)
glVertex2d(1, 0)
glVertex2d(1, 0)
glVertex2d(0.95, 0.05)
glVertex2d(1, 0)
glVertex2d(0.95, -0.05)
# y
glVertex2d(0, -1)
glVertex2d(0, 1)
glVertex2d(0, 1)
glVertex2d(0.05, 0.95)
glVertex2d(0, 1)
glVertex2d(-0.05, 0.95)
glEnd()
glFlush()
def draw():
render()
glutSwapBuffers()
def main():
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
glutInitWindowSize(300, 300)
glutCreateWindow(b"OpenGL Offscreen")
glutHideWindow()
init()
render()
glPixelStorei(GL_PACK_ALIGNMENT, 1)
data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
image = Image.frombytes("RGBA", (width, height), data)
image = ImageOps.flip(image) # in my case image is flipped top-bottom for some reason
image.save('glutout.png', 'PNG')
#glutDisplayFunc(draw)
#glutMainLoop()
main()
Run Code Online (Sandbox Code Playgroud)
我的答案(部分基于CodeSurgeon 的答案)是针对问题的第二部分。
PyOpenGL 中的离屏渲染(意味着将某些内容渲染到内部缓冲区而不是可见窗口,并将渲染的图像保存到文件或作为 http 响应传输以在网页上显示)(就像 OpenGL 本身一样)有点棘手,因为一切都是由 GLUT 完成的,所以到目前为止(创建窗口、初始化 opengl 上下文等)您现在需要手动完成,因为您不需要弹出甚至闪烁标准的 GLUT 窗口。
所以OpenGL中离屏渲染有3种方法:
1)使用GLUT进行初始化,但隐藏过剩窗口并对其进行渲染。这种方法完全独立于平台,但是在初始化过程中会出现短暂的GLUT窗口,因此它不太适合Web服务器,即但是您仍然可以将其设置为单独的Web服务器,仅在启动时进行初始化并使用一些接口进行通信它。
2) 手动创建所有内容:隐藏窗口、OpenGL 上下文和要渲染的帧缓冲区对象。这个方法很好,因为你可以控制一切并且不会出现窗口,但是上下文的创建是特定于平台的(下面是 Win64 的示例)
3)第三种方法与方法2类似,但使用WGL创建的默认Framebuffer,而不是手动创建FBO。与方法2效果相同,但更简单。如果您因其他原因不需要 FBO,那么 FBO 可能是更好的选择。
现在我将描述方法 2,即硬核方法。更多示例位于我的GitHub 存储库中。
因此离屏渲染算法包括以下步骤:
下面有完整的例子。
重要的!3.1.1 PyOpenGL实现有BUG!当你刚刚导入 WGL 时,glReadPixels() 开始崩溃
ctypes.ArgumentError: 参数 7: : 类型错误
为了避免这种情况,请转到您的包 dir\OpenGL\raw\WGL_types.py ,找到以下行
HANDLE = POINTER(None) # /home/mcfletch/pylive/OpenGL-ctypes/src/wgl.h:60
# TODO: figure out how to make the handle not appear as a void_p within the code...
HANDLE.final = True
Run Code Online (Sandbox Code Playgroud)
并将其替换为(当然对于 x64,假设对于 x86 UINT32)
HANDLE = UINT64
HANDLE.final = True
Run Code Online (Sandbox Code Playgroud)
所以有例子
from win32api import *
from win32con import *
from win32gui import *
from OpenGL.WGL import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
from PIL import ImageOps
import uuid
# =========================================
# I left here only necessary constants, it's easy to search for the rest
PFD_TYPE_RGBA = 0
PFD_MAIN_PLANE = 0
PFD_DOUBLEBUFFER = 0x00000001
PFD_DRAW_TO_WINDOW = 0x00000004
PFD_SUPPORT_OPENGL = 0x00000020
# =========================================
# OpenGL context creation helpers
def mywglCreateContext(hWnd):
pfd = PIXELFORMATDESCRIPTOR()
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL
pfd.iPixelType = PFD_TYPE_RGBA
pfd.cColorBits = 32
pfd.cDepthBits = 24
pfd.iLayerType = PFD_MAIN_PLANE
hdc = GetDC(hWnd)
pixelformat = ChoosePixelFormat(hdc, pfd)
SetPixelFormat(hdc, pixelformat, pfd)
oglrc = wglCreateContext(hdc)
wglMakeCurrent(hdc, oglrc)
# check is context created succesfully
# print "OpenGL version:", glGetString(GL_VERSION)
def mywglDeleteContext():
hrc = wglGetCurrentContext()
wglMakeCurrent(0, 0)
if hrc: wglDeleteContext(hrc)
# =========================================
# OpenGL Framebuffer Objects helpers
def myglCreateBuffers(width, height):
fbo = glGenFramebuffers(1)
color_buf = glGenRenderbuffers(1)
depth_buf = glGenRenderbuffers(1)
# binds created FBO to context both for read and draw
glBindFramebuffer(GL_FRAMEBUFFER, fbo)
# bind color render buffer
glBindRenderbuffer(GL_RENDERBUFFER, color_buf)
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_buf)
# bind depth render buffer - no need for 2D, but necessary for real 3D rendering
glBindRenderbuffer(GL_RENDERBUFFER, depth_buf)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buf)
return fbo, color_buf, depth_buf, width, height
def myglDeleteBuffers(buffers):
fbo, color_buf, depth_buf, width, height = buffers
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glDeleteRenderbuffers(1, color_buf)
glDeleteRenderbuffers(1, depth_buf)
glDeleteFramebuffers(1, fbo)
def myglReadColorBuffer(buffers):
fbo, color_buf, depth_buf, width, height = buffers
glPixelStorei(GL_PACK_ALIGNMENT, 1)
glReadBuffer(GL_COLOR_ATTACHMENT0)
data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
return data, width, height
# =========================================
# Scene rendering
def renderInit(width, height):
glClearColor(0.5, 0.5, 0.5, 1.0)
glColor(0.0, 1.0, 0.0)
gluOrtho2D(-1.0, 1.0, -1.0, 1.0)
glViewport(0, 0, width, height)
def render():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# draw xy axis with arrows
glBegin(GL_LINES)
# x
glVertex2d(-1, 0)
glVertex2d(1, 0)
glVertex2d(1, 0)
glVertex2d(0.95, 0.05)
glVertex2d(1, 0)
glVertex2d(0.95, -0.05)
# y
glVertex2d(0, -1)
glVertex2d(0, 1)
glVertex2d(0, 1)
glVertex2d(0.05, 0.95)
glVertex2d(0, 1)
glVertex2d(-0.05, 0.95)
glEnd()
glFlush()
# =========================================
# Windows stuff and main steps
def main():
# Create window first with Win32 API
hInstance = GetModuleHandle(None)
wndClass = WNDCLASS()
wndClass.lpfnWndProc = DefWindowProc
wndClass.hInstance = hInstance
wndClass.hbrBackground = GetStockObject(WHITE_BRUSH)
wndClass.hCursor = LoadCursor(0, IDC_ARROW)
wndClass.lpszClassName = str(uuid.uuid4())
wndClass.style = CS_OWNDC
wndClassAtom = RegisterClass(wndClass)
# don't care about window size, couse we will create independent buffers
hWnd = CreateWindow(wndClassAtom, '', WS_POPUP, 0, 0, 1, 1, 0, 0, hInstance, None)
# Ok, window created, now we can create OpenGL context
mywglCreateContext(hWnd)
# In OpenGL context create Framebuffer Object (FBO) and attach Color and Depth render buffers to it
width, height = 300, 300
buffers = myglCreateBuffers(width, height)
# Init our renderer
renderInit(width, height)
# Now everything is ready for job to be done!
# Render something and save it to file
render()
data, width, height = myglReadColorBuffer(buffers)
image = Image.frombytes("RGBA", (width, height), data)
image = ImageOps.flip(image) # in my case image is flipped top-bottom for some reason
# it's easy to achive antialiasing effect by resizing rendered image
# don't forget to increase initial rendered image resolution and line thikness for 2D
#image = image.resize((width/2, height/2), Image.ANTIALIAS)
image.save("fbo.png", "PNG")
# Shutdown everything
myglDeleteBuffers(buffers)
mywglDeleteContext()
main()
Run Code Online (Sandbox Code Playgroud)