如何让 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
。将该函数的句柄传递给您的渲染函数,它会渲染每一帧。太棒了!
我正在尝试使用 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
。将该函数的句柄传递给您的渲染函数,它会渲染每一帧。太棒了!