如何让 Emscripten + WebGL 在终止或请求输入之前呈现

How to get Emscripten + WebGL to render before termination or input requested

我正在尝试使用 Emscripten 获取我编写的一些旧 C++ 代码以编译为 WebAssembly 和 WebGL。我有一个非常简单的单文件 C++ 项目,它呈现 Mandelbrot 集,使用 g++ 效果很好。我 运行 它实际上确实以不断增加的细节呈现 Mandelbrot 集。

我已经使用 emcc 编译了我的代码,这需要大量阅读...但它仍然无法按我的意愿运行。出于某种原因,当我转到 HTML 页面时,在代码终止或要求输入之前不会呈现任何内容。那时,正确的帧(Mandelbrot 集)实际上是

显然我 想要 是在帧速率计时器启动之前渲染每一帧。但是我无法让它工作,即使是 glFinish() 调用。

有人可以看看我的代码,看看我是否做错了什么吗?

感谢:

// Charles Swanson, i.am.cswan@gmail.com
// Written 2019/11/30
// Compile using:
// g++ SmallestOpenGL.cpp -o SmallestOpenGL_Bin -lGL -lglfw
// emcc SmallestOpenGL.cpp -o ../IgnoreMe/SmallestOpenGL.html -s USE_GLFW=3 -s MIN_WEBGL_VERSION=2

// Copyright me, all rights reserved for now.

#define GLFW_INCLUDE_ES3
#include <GLFW/glfw3.h>
#include <iostream>
#include <time.h>
#include <thread>

const char* vertexShaderSource = "#version 300 es\n"
    "layout (location = 0) in vec3 aPos;\n"
    "out float u;\n"
    "out float v;\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "   u = aPos.x;\n"
    "   v = aPos.y;\n"
    "}[=10=]";

const char* fragmentShaderSource = "#version 300 es\n"
    "precision mediump float;\n"
    "uniform float maxiters;\n"
    "in float u;\n"
    "in float v;\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "   FragColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);\n"
    "   float scaledx = (u-0.5)*1.5f;\n"
    "   float scaledy = v*1.0f;\n"
    "   float currentx = scaledx;\n"
    "   float currenty = scaledy;\n"
    "   float currentx2 = 0.0f;\n"
    "   float currenty2 = 0.0f;\n"
    "   for (int loopind = 1;loopind<int(maxiters);loopind++)\n"
    "   {\n"
    "      currentx2 = scaledx + currentx*currentx - currenty*currenty;\n"
    "      currenty2 = scaledy + 2.0f*currentx*currenty;\n"
    "      currentx = clamp(currentx2,-10000.0f,10000.0f);\n"
    "      currenty = clamp(currenty2,-10000.0f,10000.0f);\n"
    "   }\n"
    "   float amplitude = currentx2*currentx + currenty2*currenty;\n"
    "   if (amplitude>4.0)\n"
    "       FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);\n"
    "}[=10=]";

int main(void)
{
    glfwInit();

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,0);

    GLFWwindow* window = glfwCreateWindow(400,300,"The Window Title",NULL,NULL);
    glfwMakeContextCurrent(window);

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader,1,&vertexShaderSource,NULL);
    glCompileShader(vertexShader);

    GLchar infoLog[256];
    glGetShaderInfoLog(vertexShader,256,NULL,infoLog);
    std::cout<<"Vertex shader info log says: "<<infoLog<<std::endl;

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader,1,&fragmentShaderSource,NULL);
    glCompileShader(fragmentShader);

    glGetShaderInfoLog(fragmentShader,256,NULL,infoLog);
    std::cout<<"Fragment shader info log says: "<<infoLog<<std::endl;

    GLuint program = glCreateProgram();
    glAttachShader(program,vertexShader);
    glAttachShader(program,fragmentShader);
    glLinkProgram(program);

    glGetProgramInfoLog(program,256,NULL,infoLog);
    std::cout<<"Progam info log says: "<<infoLog<<std::endl;
    
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    float vertices[] = {
        -1.0f, -1.0f, 0.0f, // bottom left
        -1.0f,  1.0f, 0.0f, // top left 
         1.0f, -1.0f, 0.0f, // bottom right  
        -1.0f,  1.0f, 0.0f, // top left
         1.0f, -1.0f, 0.0f, // bottom right 
         1.0f, 1.0f, 0.0f, // top right 
    }; 

    GLuint VAO;
    glGenVertexArrays(1,&VAO);
    glBindVertexArray(VAO);
    glEnableVertexAttribArray(0);

    GLuint VBO;
    glGenBuffers(1,&VBO);
    glBindBuffer(GL_ARRAY_BUFFER,VBO);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

    glVertexAttribPointer(0,3,GL_FLOAT,GL_TRUE,3*sizeof(float),0);

    glUseProgram(program);

    GLuint maxitersLocation = glGetUniformLocation(program,"maxiters");
    GLuint xsizeLocation = glGetUniformLocation(program,"xsize");
    GLuint ysizeLocation = glGetUniformLocation(program,"ysize");
    std::cout<<"Maxiters location is "<<maxitersLocation<<std::endl;

    int xsize;
    int ysize;
    glfwGetWindowSize(window,&xsize,&ysize);
    std::cout<<"Window size is "<<xsize<<" x "<<ysize<<std::endl;

    glUniform1f(maxitersLocation,10);
    

    float iters = 0;
    while (iters<10)
    {
        
        iters = iters+0.1;
        if (iters>10)
                iters = 10;
        glUniform1f(maxitersLocation,iters);
        glClearColor(1,0.5,0,1);
        glClear(GL_COLOR_BUFFER_BIT);

        glDrawArrays(GL_TRIANGLES,0,6);

        glfwSwapBuffers(window);
        glfwPollEvents();
        glFinish();
        std::this_thread::sleep_for(std::chrono::milliseconds(long(30)));
        
    }

    char in;
    std::cin>>in;
    glfwTerminate();
}

好的,我明白了。许多 Web 示例实现了该解决方案;我想我掩盖了他们。这里有一些:

https://github.com/pcbaecker/example-emscripten-webgl

https://gist.github.com/mortennobel/0e9e90c9bbc61cc99d5c3e9c038d8115

https://gist.github.com/ousttrue/0f3a11d5d28e365b129fe08f18f4e141

https://github.com/QafooLabs/emscripten-opengl-example

关键是使用Emscripten.h中实现的函数emscripten_set_main_loop。将该函数的句柄传递给您的渲染函数,它会渲染每一帧。太棒了!