Skip to content

Instantly share code, notes, and snippets.

@robertleeplummerjr
Last active February 24, 2019 18:27
Show Gist options
  • Save robertleeplummerjr/3313587571dd2a8fa3c3907e451b5564 to your computer and use it in GitHub Desktop.
Save robertleeplummerjr/3313587571dd2a8fa3c3907e451b5564 to your computer and use it in GitHub Desktop.
DrawBuffers WebGL, WebGL2, HeadlessGL, GoogleAngle
// 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);
}
};
// 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();
<!--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>
<!--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>
@robertleeplummerjr
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment