Last active
February 24, 2019 18:27
-
-
Save robertleeplummerjr/3313587571dd2a8fa3c3907e451b5564 to your computer and use it in GitHub Desktop.
DrawBuffers WebGL, WebGL2, HeadlessGL, GoogleAngle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// works good | |
#include <algorithm> | |
#include <vector> | |
#include <map> | |
#include <utility> | |
#include <node.h> | |
#include "nan.h" | |
#include <v8.h> | |
#include <EGL/egl.h> | |
#include <GLES2/gl2.h> | |
#include <GLES2/gl2ext.h> | |
struct TestGoogleAngle { | |
static GLenum* writeTextures() { | |
GLchar* vs[] = { | |
"void main() {\n\ | |
gl_PointSize = 300.0;\n\ | |
gl_Position = vec4(0, 0, 0, 1);\n\ | |
}" | |
}; | |
GLchar* fs[] = { | |
"#extension GL_EXT_draw_buffers : require\n\ | |
precision mediump float;\n\ | |
void main() {\n\ | |
gl_FragData[0] = vec4(1.0, .5, .3, .7);\n\ | |
gl_FragData[1] = vec4(.6, .5, .4, .3);\n\ | |
gl_FragData[2] = vec4(.2, .8, .0, 1);\n\ | |
gl_FragData[3] = vec4(.3, .4, .9, .6);\n\ | |
}" | |
}; | |
GLuint vertShader = (reinterpret_cast<PFNGLCREATESHADERPROC>(eglGetProcAddress("glCreateShader")))(GL_VERTEX_SHADER); | |
(reinterpret_cast<PFNGLSHADERSOURCEPROC>(eglGetProcAddress("glShaderSource")))(vertShader, 1, vs, nullptr); | |
(reinterpret_cast<PFNGLCOMPILESHADERPROC>(eglGetProcAddress("glCompileShader")))(vertShader); | |
GLuint fragShader = (reinterpret_cast<PFNGLCREATESHADERPROC>(eglGetProcAddress("glCreateShader")))(GL_FRAGMENT_SHADER); | |
(reinterpret_cast<PFNGLSHADERSOURCEPROC>(eglGetProcAddress("glShaderSource")))(fragShader, 1, fs, nullptr); | |
(reinterpret_cast<PFNGLCOMPILESHADERPROC>(eglGetProcAddress("glCompileShader")))(fragShader); | |
GLint vertCompileResult; | |
(reinterpret_cast<PFNGLGETSHADERIVPROC>(eglGetProcAddress("glGetShaderiv")))(vertShader, GL_COMPILE_STATUS, &vertCompileResult); | |
if (vertCompileResult == GL_FALSE) { | |
char *infolog = new char[vertCompileResult]; | |
(reinterpret_cast<PFNGLGETSHADERINFOLOGPROC>(eglGetProcAddress("glGetShaderInfoLog")))(vertShader, vertCompileResult, nullptr, infolog); | |
throw strcat("Error compiling vertex shader: ", infolog); | |
} | |
GLint fragCompileResult; | |
(reinterpret_cast<PFNGLGETSHADERIVPROC>(eglGetProcAddress("glGetShaderiv")))(fragShader, GL_COMPILE_STATUS, &fragCompileResult); | |
if (fragCompileResult == GL_FALSE) { | |
char *infolog = new char[fragCompileResult]; | |
(reinterpret_cast<PFNGLGETSHADERINFOLOGPROC>(eglGetProcAddress("glGetShaderInfoLog")))(fragShader, fragCompileResult, nullptr, infolog); | |
throw strcat("Error compiling fragment shader: ", infolog); | |
} | |
GLuint program = (reinterpret_cast<PFNGLCREATEPROGRAMPROC>(eglGetProcAddress("glCreateProgram")))(); | |
(reinterpret_cast<PFNGLATTACHSHADERPROC>(eglGetProcAddress("glAttachShader")))(program, vertShader); | |
(reinterpret_cast<PFNGLATTACHSHADERPROC>(eglGetProcAddress("glAttachShader")))(program, fragShader); | |
(reinterpret_cast<PFNGLLINKPROGRAMPROC>(eglGetProcAddress("glLinkProgram")))(program); | |
GLenum textures[] = {}; | |
GLuint fb; | |
(reinterpret_cast<PFNGLGENFRAMEBUFFERSPROC>(eglGetProcAddress("glGenFramebuffers")))(1, &fb); | |
(reinterpret_cast<PFNGLBINDFRAMEBUFFERPROC>(eglGetProcAddress("glBindFramebuffer")))(GL_FRAMEBUFFER, fb); | |
for (int i = 0; i < 4; ++i) { | |
GLuint texture; | |
(reinterpret_cast<PFNGLGENTEXTURESPROC>(eglGetProcAddress("glGenTextures")))(1, &texture); | |
textures[i] = texture; | |
(reinterpret_cast<PFNGLBINDTEXTUREPROC>(eglGetProcAddress("glBindTexture")))(GL_TEXTURE_2D, texture); | |
int width = 1; | |
int height = 1; | |
int level = 0; | |
(reinterpret_cast<PFNGLTEXIMAGE2DPROC>(eglGetProcAddress("glTexImage2D")))(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, | |
GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
// attach texture to framebuffer | |
(reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DPROC>(eglGetProcAddress("glFramebufferTexture2D")))(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT + i, | |
GL_TEXTURE_2D, texture, level); | |
} | |
// // our framebuffer textures are only 1x1 pixels | |
(reinterpret_cast<PFNGLVIEWPORTPROC>(eglGetProcAddress("glViewport")))(0, 0, 1, 1); | |
// tell it we want to draw to all 4 attachments | |
GLenum drawBuffers[] = { | |
GL_COLOR_ATTACHMENT0_EXT, | |
GL_COLOR_ATTACHMENT1_EXT, | |
GL_COLOR_ATTACHMENT2_EXT, | |
GL_COLOR_ATTACHMENT3_EXT | |
}; | |
(reinterpret_cast<PFNGLDRAWBUFFERSEXTPROC>(eglGetProcAddress("glDrawBuffersEXT")))(4, drawBuffers); | |
// draw a single point | |
(reinterpret_cast<PFNGLUSEPROGRAMPROC>(eglGetProcAddress("glUseProgram")))(program); | |
(reinterpret_cast<PFNGLDRAWARRAYSPROC>(eglGetProcAddress("glDrawArrays")))(GL_POINTS, 0, 1); | |
return textures; | |
} | |
static void renderTextures(GLenum* textures, int width, int height) { | |
GLchar const* vs[] = { | |
"void main() {\n\ | |
gl_PointSize = 300.0;\n\ | |
gl_Position = vec4(0, 0, 0, 1);\n\ | |
}" | |
}; | |
// render the 4 textures | |
GLchar const* fs[] = { | |
"precision mediump float;\n\ | |
uniform sampler2D tex[4];\n\ | |
void main() {\n\ | |
vec4 color = vec4(0);\n\ | |
for (int i = 0; i < 4; ++i) {\n\ | |
float x = gl_PointCoord.x * 4.0;\n\ | |
float amount = step(float(i), x) * step(x, float(i + 1));\n\ | |
color = mix(color, texture2D(tex[i], vec2(0)), amount);\n\ | |
}\n\ | |
gl_FragColor = vec4(color.rgb, 1.0);\n\ | |
}" | |
}; | |
GLuint vertShader = (reinterpret_cast<PFNGLCREATESHADERPROC>(eglGetProcAddress("glCreateShader")))(GL_VERTEX_SHADER); | |
(reinterpret_cast<PFNGLSHADERSOURCEPROC>(eglGetProcAddress("glShaderSource")))(vertShader, 1, vs, nullptr); | |
(reinterpret_cast<PFNGLCOMPILESHADERPROC>(eglGetProcAddress("glCompileShader")))(vertShader); | |
GLuint fragShader = (reinterpret_cast<PFNGLCREATESHADERPROC>(eglGetProcAddress("glCreateShader")))(GL_FRAGMENT_SHADER); | |
(reinterpret_cast<PFNGLSHADERSOURCEPROC>(eglGetProcAddress("glShaderSource")))(fragShader, 1, fs, nullptr); | |
(reinterpret_cast<PFNGLCOMPILESHADERPROC>(eglGetProcAddress("glCompileShader")))(fragShader); | |
GLint vertCompileResult; | |
(reinterpret_cast<PFNGLGETSHADERIVPROC>(eglGetProcAddress("glGetShaderiv")))(vertShader, GL_COMPILE_STATUS, &vertCompileResult); | |
if (vertCompileResult == GL_FALSE) { | |
char *infolog = new char[vertCompileResult]; | |
(reinterpret_cast<PFNGLGETSHADERINFOLOGPROC>(eglGetProcAddress("glGetShaderInfoLog")))(vertShader, vertCompileResult, nullptr, infolog); | |
throw strcat("Error compiling vertex shader: ", infolog); | |
} | |
GLint fragCompileResult; | |
(reinterpret_cast<PFNGLGETSHADERIVPROC>(eglGetProcAddress("glGetShaderiv")))(fragShader, GL_COMPILE_STATUS, &fragCompileResult); | |
if (fragCompileResult == GL_FALSE) { | |
char *infolog = new char[fragCompileResult]; | |
(reinterpret_cast<PFNGLGETSHADERINFOLOGPROC>(eglGetProcAddress("glGetShaderInfoLog")))(fragShader, fragCompileResult, nullptr, infolog); | |
throw strcat("Error compiling fragment shader: ", infolog); | |
} | |
GLuint program = (reinterpret_cast<PFNGLCREATEPROGRAMPROC>(eglGetProcAddress("glCreateProgram")))(); | |
(reinterpret_cast<PFNGLATTACHSHADERPROC>(eglGetProcAddress("glAttachShader")))(program, vertShader); | |
(reinterpret_cast<PFNGLATTACHSHADERPROC>(eglGetProcAddress("glAttachShader")))(program, fragShader); | |
(reinterpret_cast<PFNGLLINKPROGRAMPROC>(eglGetProcAddress("glLinkProgram")))(program); | |
(reinterpret_cast<PFNGLBINDFRAMEBUFFERPROC>(eglGetProcAddress("glBindFramebuffer")))(GL_FRAMEBUFFER, 0); | |
(reinterpret_cast<PFNGLVIEWPORTPROC>(eglGetProcAddress("glViewport")))(0, 0, width, height); | |
(reinterpret_cast<PFNGLUSEPROGRAMPROC>(eglGetProcAddress("glUseProgram")))(program); | |
// binds all the textures and set the uniforms | |
for (int i = 0; i < 4; i++) { | |
GLenum texture = textures[i]; | |
(reinterpret_cast<PFNGLACTIVETEXTUREPROC>(eglGetProcAddress("glActiveTexture")))(GL_TEXTURE0 + i); | |
(reinterpret_cast<PFNGLBINDTEXTUREPROC>(eglGetProcAddress("glBindTexture")))(GL_TEXTURE_2D, texture); | |
} | |
GLint values[4] = {0, 1, 2, 3}; | |
(reinterpret_cast<PFNGLUNIFORM1IVPROC>(eglGetProcAddress("glUniform1iv")))(reinterpret_cast<PFNGLGETUNIFORMLOCATIONPROC>(eglGetProcAddress("glGetUniformLocation"))(program, "tex[0]"), 4, values); | |
(reinterpret_cast<PFNGLDRAWARRAYSPROC>(eglGetProcAddress("glDrawArrays")))(GL_POINTS, 0, 1); | |
} | |
static GLubyte* toPixels(int width, int height) { | |
GLubyte* bytes = new GLubyte[width * height * 4]; | |
(reinterpret_cast<PFNGLREADPIXELSPROC>(eglGetProcAddress("glReadPixels")))(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bytes); | |
return bytes; | |
} | |
static void checkPixels(GLubyte* bytes, int width, int height) { | |
int notZero = 0; | |
int count = width * height * 4; | |
for (int i = 0; i < count; i++) { | |
if (bytes[i] > 0) { | |
notZero++; | |
} | |
} | |
if (notZero != 144000) { | |
throw strcat(strcat("Only ", std::to_string(notZero).c_str()), " are not 0, expected 144000"); | |
} | |
throw "SUCCESS!"; | |
} | |
static EGLContext createContext() { | |
EGLint num_config; | |
EGLDisplay DISPLAY = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
EGLConfig config; | |
EGLSurface surface; | |
//Get display | |
if (DISPLAY == EGL_NO_DISPLAY) { | |
throw "No display"; | |
} | |
//Initialize EGL | |
if (!eglInitialize(DISPLAY, NULL, NULL)) { | |
throw "No egl"; | |
} | |
EGLint attrib_list[] = { | |
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | |
, EGL_RED_SIZE, 8 | |
, EGL_GREEN_SIZE, 8 | |
, EGL_BLUE_SIZE, 8 | |
, EGL_ALPHA_SIZE, 8 | |
, EGL_DEPTH_SIZE, 24 | |
, EGL_STENCIL_SIZE, 8 | |
, EGL_NONE | |
}; | |
if (!eglChooseConfig( | |
DISPLAY, | |
attrib_list, | |
&config, | |
1, | |
&num_config) || | |
num_config != 1) { | |
throw "Bad config"; | |
} | |
//Create context | |
EGLint contextAttribs[] = { | |
EGL_CONTEXT_CLIENT_VERSION, 2, | |
EGL_NONE | |
}; | |
EGLContext context = eglCreateContext(DISPLAY, config, EGL_NO_CONTEXT, contextAttribs); | |
if (context == EGL_NO_CONTEXT) { | |
throw "No context"; | |
} | |
EGLint surfaceAttribs[] = { | |
EGL_WIDTH, (EGLint)300 | |
, EGL_HEIGHT, (EGLint)150 | |
, EGL_NONE | |
}; | |
surface = eglCreatePbufferSurface(DISPLAY, config, surfaceAttribs); | |
if (surface == EGL_NO_SURFACE) { | |
throw "No surface"; | |
} | |
//Set active | |
if (!eglMakeCurrent(DISPLAY, surface, surface, context)) { | |
throw "Bad state"; | |
} | |
return context; | |
} | |
static void main() { | |
int width = 300; | |
int height = 150; | |
EGLContext gl = createContext(); | |
GLenum* textures = writeTextures(); | |
renderTextures(textures, width, height); | |
GLubyte* pixels = toPixels(width, height); | |
checkPixels(pixels, width, height); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// works good | |
const headlessGL = require('./'); | |
function writeTextures(gl, drawBuffers) { | |
const vs = `void main() { | |
gl_PointSize = 300.0; | |
gl_Position = vec4(0, 0, 0, 1); | |
}`; | |
const fs = `#extension GL_EXT_draw_buffers : require | |
precision mediump float; | |
void main() { | |
gl_FragData[0] = vec4(1, .5, .3, .7); | |
gl_FragData[1] = vec4(.6, .5, .4, .3); | |
gl_FragData[2] = vec4(.2, .8, .0, 1); | |
gl_FragData[3] = vec4(.3, .4, .9, .6); | |
}`; | |
const vertShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertShader, vs); | |
gl.compileShader(vertShader); | |
const fragShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragShader, fs); | |
gl.compileShader(fragShader); | |
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); | |
} | |
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertShader); | |
gl.attachShader(program, fragShader); | |
gl.linkProgram(program); | |
const textures = []; | |
const fb = gl.createFramebuffer(); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fb); | |
for (let i = 0; i < 4; ++i) { | |
const texture = gl.createTexture(); | |
textures.push(texture); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
const width = 1; | |
const height = 1; | |
const level = 0; | |
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, | |
gl.RGBA, gl.UNSIGNED_BYTE, null); | |
// attach texture to framebuffer | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, drawBuffers.COLOR_ATTACHMENT0_WEBGL + i, | |
gl.TEXTURE_2D, texture, level); | |
} | |
// our framebuffer textures are only 1x1 pixels | |
gl.viewport(0, 0, 1, 1); | |
// tell it we want to draw to all 4 attachments | |
drawBuffers.drawBuffersWEBGL([ | |
drawBuffers.COLOR_ATTACHMENT0_WEBGL, | |
drawBuffers.COLOR_ATTACHMENT1_WEBGL, | |
drawBuffers.COLOR_ATTACHMENT2_WEBGL, | |
drawBuffers.COLOR_ATTACHMENT3_WEBGL, | |
]); | |
// draw a single point | |
gl.useProgram(program); | |
gl.drawArrays(gl.POINT, 0, 1); | |
return textures; | |
} | |
function renderTextures(gl, width, height, textures) { | |
const vs = `void main() { | |
gl_PointSize = 300.0; | |
gl_Position = vec4(0, 0, 0, 1); | |
}`; | |
// render the 4 textures | |
const fs = `precision mediump float; | |
uniform sampler2D tex[4]; | |
void main() { | |
vec4 color = vec4(0); | |
for (int i = 0; i < 4; ++i) { | |
float x = gl_PointCoord.x * 4.0; | |
float amount = step(float(i), x) * step(x, float(i + 1)); | |
color = mix(color, texture2D(tex[i], vec2(0)), amount); | |
} | |
gl_FragColor = vec4(color.rgb, 1.0); | |
}`; | |
const vertShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertShader, vs); | |
gl.compileShader(vertShader); | |
const fragShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragShader, fs); | |
gl.compileShader(fragShader); | |
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); | |
} | |
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertShader); | |
gl.attachShader(program, fragShader); | |
gl.linkProgram(program); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, null); | |
gl.viewport(0, 0, width, height); | |
gl.useProgram(program); | |
// binds all the textures and set the uniforms | |
for (let i = 0; i < textures.length; i++) { | |
const texture = textures[i]; | |
gl.activeTexture(gl.TEXTURE0 + i); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
} | |
gl.uniform1iv(gl.getUniformLocation(program, 'tex[0]'), [0,1,2,3]); | |
gl.drawArrays(gl.POINTS, 0, 1); | |
} | |
function toPixels(gl, width, height) { | |
const bytes = new Uint8Array(width * height * 4); | |
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, bytes); | |
return bytes; | |
} | |
function checkPixels(bytes) { | |
const notZero = []; | |
for (let i = 0; i < bytes.length; i++) { | |
const byte = bytes[i]; | |
if (byte > 0) { | |
notZero.push(byte); | |
} | |
} | |
if (notZero.length !== 144000) { | |
throw new Error(`Only ${notZero.length} are not 0, expected 144000`); | |
} | |
console.log('SUCCESS!'); | |
} | |
function main() { | |
const width = 300; | |
const height = 150; | |
const gl = headlessGL(width, height); | |
const drawBuffers = gl.getExtension('WEBGL_draw_buffers'); | |
if (!gl) { | |
return console.log("need WebGL"); | |
} | |
const textures = writeTextures(gl, drawBuffers); | |
renderTextures(gl, width, height, textures); | |
const pixels = toPixels(gl, width, height); | |
checkPixels(pixels); | |
} | |
main(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--works great--> | |
<html> | |
<canvas></canvas> | |
<script> | |
function writeTextures(gl, drawBuffers) { | |
const vs = `void main() { | |
gl_PointSize = 300.0; | |
gl_Position = vec4(0, 0, 0, 1); | |
}`; | |
const fs = `#extension GL_EXT_draw_buffers : require | |
precision mediump float; | |
void main() { | |
gl_FragData[0] = vec4(1, .5, .3, .7); // orange | |
gl_FragData[1] = vec4(.6, .5, .4, .3); // brown | |
gl_FragData[2] = vec4(.2, .8, .0, 1); // green | |
gl_FragData[3] = vec4(.3, .4, .9, .6); // blue | |
}`; | |
const vertShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertShader, vs); | |
gl.compileShader(vertShader); | |
const fragShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragShader, fs); | |
gl.compileShader(fragShader); | |
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); | |
} | |
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertShader); | |
gl.attachShader(program, fragShader); | |
gl.linkProgram(program); | |
const textures = []; | |
const fb = gl.createFramebuffer(); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fb); | |
for (let i = 0; i < 4; ++i) { | |
const texture = gl.createTexture(); | |
textures.push(texture); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
const width = 1; | |
const height = 1; | |
const level = 0; | |
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, | |
gl.RGBA, gl.UNSIGNED_BYTE, null); | |
// attach texture to framebuffer | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, drawBuffers.COLOR_ATTACHMENT0_WEBGL + i, | |
gl.TEXTURE_2D, texture, level); | |
} | |
// our framebuffer textures are only 1x1 pixels | |
gl.viewport(0, 0, 1, 1); | |
// tell it we want to draw to all 4 attachments | |
drawBuffers.drawBuffersWEBGL([ | |
drawBuffers.COLOR_ATTACHMENT0_WEBGL, | |
drawBuffers.COLOR_ATTACHMENT1_WEBGL, | |
drawBuffers.COLOR_ATTACHMENT2_WEBGL, | |
drawBuffers.COLOR_ATTACHMENT3_WEBGL, | |
]); | |
// draw a single point | |
gl.useProgram(program); | |
gl.drawArrays(gl.POINT, 0, 1); | |
return textures; | |
} | |
function renderTextures(gl, drawBuffers, textures) { | |
const vs = `void main() { | |
gl_PointSize = 300.0; | |
gl_Position = vec4(0, 0, 0, 1); | |
}`; | |
// render the 4 textures | |
const fs = `precision mediump float; | |
uniform sampler2D tex[4]; | |
void main() { | |
vec4 color = vec4(0); | |
for (int i = 0; i < 4; ++i) { | |
float x = gl_PointCoord.x * 4.0; | |
float amount = step(float(i), x) * step(x, float(i + 1)); | |
color = mix(color, texture2D(tex[i], vec2(0)), amount); | |
} | |
gl_FragColor = vec4(color.rgb, 1.0); | |
}`; | |
const vertShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertShader, vs); | |
gl.compileShader(vertShader); | |
const fragShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragShader, fs); | |
gl.compileShader(fragShader); | |
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); | |
} | |
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertShader); | |
gl.attachShader(program, fragShader); | |
gl.linkProgram(program); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, null); | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
gl.useProgram(program); | |
// binds all the textures and set the uniforms | |
for (let i = 0; i < textures.length; i++) { | |
const texture = textures[i]; | |
gl.activeTexture(gl.TEXTURE0 + i); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
} | |
gl.uniform1iv(gl.getUniformLocation(program, `tex[0]`), [0,1,2,3]); | |
gl.drawArrays(gl.POINTS, 0, 1); | |
} | |
function toPixels(gl) { | |
const bytes = new Uint8Array(gl.canvas.width * gl.canvas.height * 4); | |
gl.readPixels(0, 0, gl.canvas.width, gl.canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, bytes); | |
return bytes; | |
} | |
function checkPixels(bytes) { | |
const notZero = []; | |
for (let i = 0; i < bytes.length; i++) { | |
const byte = bytes[i]; | |
if (byte > 0) { | |
notZero.push(byte); | |
} | |
} | |
if (notZero.length !== 144000) { | |
throw new Error(`Only ${notZero.length} are not 0, expected 144000`); | |
} | |
console.log('SUCCESS!'); | |
} | |
function main() { | |
const gl = document.querySelector('canvas').getContext('webgl'); | |
const drawBuffers = gl.getExtension('WEBGL_draw_buffers'); | |
if (!gl) { | |
return alert("need WebGL"); | |
} | |
const textures = writeTextures(gl, drawBuffers); | |
renderTextures(gl, drawBuffers, textures); | |
const pixels = toPixels(gl); | |
checkPixels(pixels); | |
} | |
main(); | |
</script> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--works great--> | |
<html> | |
<canvas></canvas> | |
<script> | |
function writeTextures(gl) { | |
const vs = `#version 300 es | |
void main() { | |
gl_PointSize = 300.0; | |
gl_Position = vec4(0, 0, 0, 1); | |
}`; | |
const fs = `#version 300 es | |
precision mediump float; | |
layout(location = 0) out vec4 outColor0; | |
layout(location = 1) out vec4 outColor1; | |
layout(location = 2) out vec4 outColor2; | |
layout(location = 3) out vec4 outColor3; | |
void main() { | |
outColor0 = vec4(1, .5, .3, .7); // orange | |
outColor1 = vec4(.6, .5, .4, .3); // brown | |
outColor2 = vec4(.2, .8, .0, 1); // green | |
outColor3 = vec4(.3, .4, .9, .6); // blue | |
}`; | |
const vertShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertShader, vs); | |
gl.compileShader(vertShader); | |
const fragShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragShader, fs); | |
gl.compileShader(fragShader); | |
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); | |
} | |
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertShader); | |
gl.attachShader(program, fragShader); | |
gl.linkProgram(program); | |
const textures = []; | |
const fb = gl.createFramebuffer(); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fb); | |
for (let i = 0; i < 4; ++i) { | |
const texture = gl.createTexture(); | |
textures.push(texture); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
const width = 1; | |
const height = 1; | |
const level = 0; | |
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, | |
gl.RGBA, gl.UNSIGNED_BYTE, null); | |
// attach texture to framebuffer | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, | |
gl.TEXTURE_2D, texture, level); | |
} | |
// our framebuffer textures are only 1x1 pixels | |
gl.viewport(0, 0, 1, 1); | |
// tell it we want to draw to all 4 attachments | |
gl.drawBuffers([ | |
gl.COLOR_ATTACHMENT0, | |
gl.COLOR_ATTACHMENT1, | |
gl.COLOR_ATTACHMENT2, | |
gl.COLOR_ATTACHMENT3, | |
]); | |
// draw a single point | |
gl.useProgram(program); | |
gl.drawArrays(gl.POINT, 0, 1); | |
return textures; | |
} | |
function renderTextures(gl, textures) { | |
const vs = `#version 300 es | |
void main() { | |
gl_PointSize = 300.0; | |
gl_Position = vec4(0, 0, 0, 1); | |
}`; | |
// render the 4 textures | |
const fs = `#version 300 es | |
precision mediump float; | |
uniform sampler2D tex[4]; | |
out vec4 outColor; | |
void main() { | |
vec4 color = vec4(0); | |
for (int i = 0; i < 4; ++i) { | |
float x = gl_PointCoord.x * 4.0; | |
float amount = step(float(i), x) * step(x, float(i + 1)); | |
color = mix(color, texture(tex[i], vec2(0)), amount); | |
} | |
outColor = vec4(color.rgb, 1); | |
}`; | |
const vertShader = gl.createShader(gl.VERTEX_SHADER); | |
gl.shaderSource(vertShader, vs); | |
gl.compileShader(vertShader); | |
const fragShader = gl.createShader(gl.FRAGMENT_SHADER); | |
gl.shaderSource(fragShader, fs); | |
gl.compileShader(fragShader); | |
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling vertex shader: ' + gl.getShaderInfoLog(vertShader)); | |
} | |
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { | |
throw new Error('Error compiling fragment shader: ' + gl.getShaderInfoLog(fragShader)); | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertShader); | |
gl.attachShader(program, fragShader); | |
gl.linkProgram(program); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, null); | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
gl.useProgram(program); | |
// binds all the textures and set the uniforms | |
for (let i = 0; i < textures.length; i++) { | |
const texture = textures[i]; | |
gl.activeTexture(gl.TEXTURE0 + i); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
} | |
gl.uniform1iv(gl.getUniformLocation(program, `tex[0]`), [0,1,2,3]); | |
gl.drawArrays(gl.POINTS, 0, 1); | |
} | |
function toPixels(gl) { | |
const bytes = new Uint8Array(gl.canvas.width * gl.canvas.height * 4); | |
gl.readPixels(0, 0, gl.canvas.width, gl.canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, bytes); | |
return bytes; | |
} | |
function checkPixels(bytes) { | |
const notZero = []; | |
for (let i = 0; i < bytes.length; i++) { | |
const byte = bytes[i]; | |
if (byte > 0) { | |
notZero.push(byte); | |
} | |
} | |
if (notZero.length !== 144000) { | |
throw new Error(`Only ${notZero.length} are not 0, expected 144000`); | |
} | |
console.log('SUCCESS!'); | |
} | |
function main() { | |
const gl = document.querySelector('canvas').getContext('webgl2'); | |
if (!gl) { | |
return alert("need WebGL2"); | |
} | |
const textures = writeTextures(gl); | |
renderTextures(gl, textures); | |
const pixels = toPixels(gl); | |
checkPixels(pixels); | |
} | |
main(); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Original example from https://stackoverflow.com/a/51798078/1324039