2016-12-13 12 views
0

저는 PYopenGL을 처음 사용합니다. 사실, PYopenGL이 내 작업에 적합한 방법인지 잘 모르겠습니다.JPG 이미지에 PYopenGL을 쓰는 방법

Wavefront obj 파일 형식의 3D 모델이 있습니다. 주어진 뷰에서 모델의 "printscreen"을 가져와야합니다. 즉, 모델을 렌더링하고 모델을 디스플레이 대신 이미지 (jpg)로 저장해야합니다.

제 생각에는이 작업에 PYopenGL을 사용하는 것이 었습니다. 그러나, 내가 어떻게 제안이나 예제를 찾을 수있는 인터넷 검색을 수행합니다. 따라서 PYopenGL이 내 작업에 적합한 도구 인 경우 의심을 갖기 시작합니다.

여러분 중 누군가가 이미 이와 비슷한 것을 알고 있거나 배울 수있는 예를 알고 있습니까?

미리 감사드립니다.

미치

+2

안녕하세요! 답변이없는 질문이나 투표를 피하려면 http://stackoverflow.com/help/how-to-ask를 참조하십시오. 귀하의 질문은 매우 광범위하며, 여기서 우리가 중요하게 생각하는 것은 문제를 해결하기위한 사전 노력을하고 보여주는 것입니다. 따라서 지금까지 시도한 것을 보여주는 코드를 추가하는 것이 좋습니다. 아직 무언가를 시도하지 않았다면, 우리는 당신을 위해 당신의 일을하기 위해 여기에 있지 않습니다. 당신은 "너무 광범위합니다"라고 표시 될 수 있습니다. 그러나 충분한 정보를 가진 사람이 이것을보고 길을 안내 할 것입니다. – gelliott181

답변

0
당신이 한 이미지에 특정 각도에서 3D 모델/장면을 렌더링하고 관심있는 모든 당신이는 OpenGL 창을 열고 실행 가진 마음을하지 않는 경우 PyOpenGL이 당신의 목적으로 사용할 수

.

.obj 형식의 파서를 작성하는 방법을 온라인에서 여러 곳에서 볼 수 있습니다. 위키 백과 문서를 here 형식으로 보는 것 외에도 pygame website에 고정 함수 obj 로더의 구현을 찾을 수 있다고 생각합니다. .obj 모델을 직접 만들고 있다면 사양이 꽤 느슨하고 파서를 작성하기가 어려울 수 있습니다. 또는 Assimp과 같은 라이브러리를 사용하여 모델을로드하고 데이터를 추출 할 수 있습니다.이 파일에는 파이썬 pyAssimp 바인딩이 있습니다.

스크린 샷을 저장하는 것과 같이 3D 장면을 텍스처로 렌더링해야합니다. this answer을 살펴 보는 것이 좋습니다. 당신은 당신이 오프 스크린 렌더링을하고 싶다면 FBOs (Frame Buffer Objects)를 사용하는 방법뿐만 아니라 glReadPixels를 사용하는 방법을 배워야 할 것입니다.

1

내 대답은 부분적으로 CodeSurgeon's answer을 기준으로 한 질문의 두 번째 부분입니다.

오프 스크린 렌더링 (표시되는 창 대신 내부 버퍼에 무엇인가를 렌더링하고 렌더링 된 이미지를 파일로 저장하거나 웹 페이지에 표시 할 HTTP 응답으로 저장). PyOpenGL에서 (OpenGL 자체 에서처럼) 약간 까다 롭습니다. 왜냐하면 당신이 표준 GLUT 윈도우를 팝업하거나 깜박일 필요가 없기 때문에 지금까지는 모든 것이 GLUT에 의해 행해졌 기 때문에 (창, init OpenGL 컨텍스트 생성 등) 손으로해야합니다.

그래서는 OpenGL에서 오프 스크린 렌더링을위한 3 가지 방법이 있습니다 :

1) 초기화 GLUT를 사용하지만, 과잉 창을 숨기고 그것을 렌더링합니다. 이 방법은 플랫폼에 독립적입니다. 그러나 GLUT 윈도우는 초기화하는 동안 짧은 시간 동안 나타납니다. 따라서 웹 서버에는 적합하지 않습니다. 그렇지만 시작시에만 초기화를 수행하고 통신 할 별도의 인터페이스를 사용하는 별도의 웹 서버로 설정할 수 있습니다. 그것.

2) 렌더링 할 숨겨진 창, OpenGL 컨텍스트 및 Framebuffer 개체를 모두 수동으로 만듭니다. 이 방법은 모든 것을 제어하고 윈도우는 나타나지 않지만 컨텍스트 생성은 플랫폼에 따라 다릅니다. (아래는 Win64의 예입니다)

3) 세 번째 방법은 방법 2와 비슷하지만 대신 WGL에서 만든 기본 Framebuffer를 사용합니다 손으로 FBO를 만드는 방법. 방법 2와 동일한 효과가 있지만 더 간단합니다.다른 이유로 FBO가 필요하지 않은 경우, FBO가 더 바람직 할 수 있습니다.

이제 방법 2, 하드 코어 1에 대해 설명하겠습니다. 더 많은 샘플은 내 GitHub repository에 있습니다.

그래서 오프 스크린 렌더링 알고리즘은 다음 단계로 구성

  1. 하는 창을 필요로하기 때문에, 심지어는 OpenGL 컨텍스트를 만들 에 숨겨진 숨겨진 창을 만들기
  2. 는 OpenGL 컨텍스트를 작성
  3. 프레임 버퍼 객체를 생성 (FBO)
  4. 렌더링 버퍼 (색상 및 깊이)를 생성하고 FBO에 첨부하십시오 (자세한 내용은 FBO manual 참조)
  5. FBO에서 FBO로 바인딩 렌더링을위한 OpenGL 컨텍스트
  6. 무언가 렌더링. 이 예제에서는 단순화를 위해 2D 프리미티브만을 사용하지만 깊이 테스트를 사용하여 3D 렌더링을위한 버퍼를 준비합니다
  7. 읽기 용 설정 버퍼, 여기서는 FBO가 하나이므로
  8. 에서 읽을 버퍼를 선택하지 않아도됩니다.
  9. glReadPixels()을 사용하여 색상 렌더링 버퍼에서 렌더링 된 데이터 읽기
  10. 수신 된 데이터로 원하는대로 수행하십시오. 즉, 수신 된 데이터로 원하는대로 수행하십시오. 즉, PIL 이미지를 만들어 파일에 저장하십시오. 또한 이중 해상도로 렌더링하고 PIL 이미지의 크기를 조정하여 앤티 앨리어스 효과를 적용 할 수 있습니다.

그래서 전체 예제가 있습니다.

중요! 3.1.1 PyOpenGL 구현에는 버그가 있습니다! 인수 7 : : : 그냥 WGL, glReadPixels()

ctypes.ArgumentError과 충돌하기 시작 가져올 때 잘못된 유형을

이 당신의 패키지 디렉토리 \ OpenGL을 \ 원시 \ 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 

하고 (물론 64를 들면, 86 UINT32에 대한 가정)로 교체

HANDLE = UINT64 
HANDLE.final = True 

그래서 예를

012,351,641있다
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() 
0

GLUT 숨겨진 창 방법은 훨씬 간단하고 플랫폼 독립적이지만 창 깜박임으로 연결됩니다.

Django 용으로 설정하려면 렌더러를 별도의 웹 서버로 구현할 수 있습니다. 렌더러는 시작시 한 번만 창에 깜박이고 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()