首页 > 解决方案 > 将 JavaScript/WebGL 移植到 Python/OpenGL

问题描述

我正在将 node/javascript 中的一些 WebGL 代码移植到 python3 中的 PyOpenGL 代码。尽管 python 代码确实打开了一个 OpenGL 窗口,但它并没有显示带有网格图案的矩形。为了排除故障,我已将程序减少到最低限度,但找不到问题所在。任何想法/指针?

在 python 代码下方和原始 javascript 代码下方。

Python3:

import OpenGL
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
from OpenGL.GL.framebufferobjects import *
import numpy

class Viewport:
    base = None
    # Model object
    window=None
    windowsize=(256*3,144*3)

    def buildShader(self,txt, type):
        #print ("buildShader",txt,type)
        s = glCreateShader(type)
        glShaderSource(s, txt)
        glCompileShader(s)

        if (not glGetShaderiv(s, GL_COMPILE_STATUS)):
            raise RuntimeError ("Could not compile shader:" , glGetShaderInfoLog(s))
        return s

    def setUniforms(self,prog,modelvar,names):
        modelvar['uniform'] = {}
        for u in names:
            modelvar['uniform'][u] = glGetUniformLocation(prog, u)
        return modelvar

    def setAttribs(self,prog, modelvar,names):
        modelvar['attrib'] = {}
        for attrib in names:
            modelvar['attrib'][attrib]=glGetAttribLocation(prog,attrib)
        return modelvar

    def makeProgram(self,modelvar,vert, frag, uniforms, attribs):
        v = self.buildShader(vert, GL_VERTEX_SHADER)
        f = self.buildShader(frag, GL_FRAGMENT_SHADER)

        prog = glCreateProgram()
        glAttachShader(prog, v)
        glAttachShader(prog, f)
        glLinkProgram(prog)

        #if (not glGetProgramParameter(prog, GL_LINK_STATUS)):
        if (not glGetProgramiv(prog, GL_LINK_STATUS)):
            raise RuntimeError("Could not link program:" + glGetProgramInfoLog(prog))

        self.setUniforms(prog, modelvar, uniforms)
        self.setAttribs(prog, modelvar, attribs)

        return prog

    def makeBase(self):
        self.base = {}
        self.base['prog'] = self.makeProgram(
            self.base,
            open('base.vert').read(),
            open('base.frag').read(),
            ['view', 'zmin', 'aspect'], ['v'])

        self.base['vert'] = glGenBuffers(1) #createBuffer()
        glBindBuffer(GL_ARRAY_BUFFER, self.base['vert'])
        glBufferData(
            GL_ARRAY_BUFFER,
            numpy.array([-1, -1,
                        -1,  1,
                        1, -1,
                        1,  1],dtype=numpy.float32),
            GL_STATIC_DRAW)

        self.base['frac'] = 0.5
        return self.base

    def drawBase(self,base):
        glUseProgram(self.base['prog'])
        M=numpy.array([ 0.26 , 0.22 , 0.18 , 0, -0.4 , 0.13 , 0.11 , 0, 0 , 0.42 , -0.1 , 0, 0 , 0 , 0 , 1])
        glUniformMatrix4fv(self.base['uniform']['view'], 1, False, M)#self.viewMatrix())
        printer_aspectRatio=1.6
        glUniform1f(self.base['uniform']['aspect'], printer_aspectRatio)

        glBindBuffer(GL_ARRAY_BUFFER, self.base['vert'])
        glEnableVertexAttribArray(self.base['attrib']['v'])
        glVertexAttribPointer(self.base['attrib']['v'], 2, GL_FLOAT, False, 0, 0)

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)


    def draw(self):

        glClearColor(1, 1, 1, 1)
        glClear(GL_COLOR_BUFFER_BIT)
        self.drawBase(self.base)
        glutSwapBuffers()



    def __init__(self):

        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)

        glutInitWindowSize(self.windowsize[0],self.windowsize[1])    
        glutInitWindowPosition(0,0)    
        self.window=glutCreateWindow('hackathon-slicer')
        glutDisplayFunc(self.draw)

        glEnable(GL_DEPTH_TEST)

        self.base=self.makeBase()

        glutMainLoop()
        self.draw()

文件 base.frag:

varying /*mediump*/ vec2 uv;

void main() {
   /*mediump*/ float u = mod(uv.x * 10.0, 1.0);
   /*mediump*/ float v = mod(uv.y * 10.0, 1.0);
   /*mediump*/ float t = (u > 0.1 && u < 0.9 &&
                   v > 0.1 && v < 0.9) ? 0.3 : 0.5;
   gl_FragColor = vec4(t, t, t, 1);
}

文件base.vert:

attribute vec2 v; // Vertex position

uniform mat4 view; // Model transform matrix
uniform /*mediump*/ float zmin; // Z position of plane
uniform /*mediump*/ float aspect; // Aspect ratio

varying /*mediump*/ vec2 uv;

void main() {
   gl_Position = view * vec4(v.x * aspect, v.y, zmin - 0.01, 1);
   gl_Position.w = (gl_Position.z + 1.0);
   uv = (vec2(v.x * aspect, v.y) + 1.0) / 2.0;
}

javascript 代码使用相同的 base.frag 和 vert 文件,但是没有注释掉 mediump 单词。

原始的javascript代码:

'use strict';

let _ = require('underscore');
let glslify = require('glslify');

let ui = require('./ui.js');
let printer = require('./printer.js');

let canvas = document.getElementById("canvas");
let gl = canvas.getContext("experimental-webgl");

let base = makeBase();

function buildShader(txt, type){
    let s = gl.createShader(type);
    gl.shaderSource(s, txt);
    gl.compileShader(s);
    return s;
}

function setUniforms(prog, u){
    prog.uniform = {};
    _.each(u, function(u){ prog.uniform[u] = gl.getUniformLocation(prog, u); });
}

function setAttribs(prog, a){
    prog.attrib = {};
    _.each(a, function(a){ prog.attrib[a] = gl.getAttribLocation(prog, a); });
}

function makeProgram(vert, frag, uniforms, attribs){
    let v = buildShader(vert, gl.VERTEX_SHADER);
    let f = buildShader(frag, gl.FRAGMENT_SHADER);

    let prog = gl.createProgram();
    gl.attachShader(prog, v);
    gl.attachShader(prog, f);
    gl.linkProgram(prog);

    setUniforms(prog, uniforms);
    setAttribs(prog, attribs);

    return prog;
}

function makeBase(){
    let base = {};
    base.prog = makeProgram(
        glslify(__dirname + '/../shaders/base.vert'),
        glslify(__dirname + '/../shaders/base.frag'),
        ['view', 'zmin', 'aspect'], ['v']);

    base.vert = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, base.vert);
    gl.bufferData(
        gl.ARRAY_BUFFER,
        new Float32Array([-1, -1,
                          -1,  1,
                           1, -1,
                           1,  1]),
        gl.STATIC_DRAW);

    base.frac = 0.5;
    return base;
}


function drawBase(base){
    gl.useProgram(base.prog);
    let M=[ 0.26 , 0.22 , 0.18 , 0, -0.4 , 0.13 , 0.11 , 0, 0 , 0.42 , -0.1 , 0, 0 , 0 , 0 , 1];
    gl.uniformMatrix4fv(base.prog.uniform.view, false, M);
    gl.uniform1f(base.prog.uniform.aspect, printer.aspectRatio());

    gl.bindBuffer(gl.ARRAY_BUFFER, base.vert);
    gl.enableVertexAttribArray(base.prog.attrib.v);
    gl.vertexAttribPointer(base.prog.attrib.v, 2, gl.FLOAT, false, 0, 0);

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}


function draw(){
    gl.clearColor(1, 1, 1, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    drawBase(base);
}

function init(){
    gl.enable(gl.DEPTH_TEST);
    draw();
}

module.exports = {'init': init};

标签: javascriptpythonnode.jsopenglwebgl

解决方案


如果绑定了数组缓冲区,则第 6 个参数glVertexAttribPointer被视为缓冲区对象数据存储中的字节偏移量。但是参数的类型必须是指针。

在您的情况下,偏移量为 0。这意味着,您要么必须使用内置常量None

glVertexAttribPointer(self.base['attrib']['v'], 2, GL_FLOAT, False, 0, None)

或者您必须使用 a ctypes.cast,它将 0 转换为 type ctypes.c_void_p

glVertexAttribPointer(self.base['attrib']['v'], 2, GL_FLOAT, False, 0,
    ctypes.cast(0, ctypes.c_void_p))

由于深度测试由

glEnable(GL_DEPTH_TEST)

您还必须在每一帧开始时清除深度缓冲区:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )

推荐阅读