Related
I want to load textures without callbacks like .onload(). I am trying to use texture from blob: https://plnkr.co/edit/n4CbWMEOAyJMQFYy
const response = await fetch("./assets/texture.png");
const imageBlob = await response.blob();
const imageBitmap = await createImageBitmap(imageBlob);
console.log(imageBitmap);
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, imageBitmap);
Expected result is:
But I have this:
I can see in the Network tab that the image has been uploaded:
This line of code console.log(imageBitmap); says that the image exists:
texImage2D() documentation says that I can use ImageBitmap as source.
Updated
I tried to use the loadImage function from this answer: https://stackoverflow.com/a/52060802/4159530 Playground: https://plnkr.co/edit/n4CbWMEOAyJMQFYy
function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener('load', () => {
resolve(image);
});
image.src = url;
});
}
/* ... */
async function init() {
/* ... */
const image = await loadImage("./assets/texture.png");
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
/* ... */
}
But that didn't solve the problem.
This answer helped me to solve the problem: https://stackoverflow.com/a/52060802/4159530
Playground: https://plnkr.co/edit/n4CbWMEOAyJMQFYy
function loadImage(url) {
return new Promise(resolve => {
const image = new Image();
image.addEventListener("load", () => {
resolve(image);
});
image.src = url;
});
}
/* ... */
async function init() {
/* ... */
const image = await loadImage("./assets/texture.png");
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
draw();
/* ... */
}
As you can see, I call the draw() function after the gl.texImage2D() function. I don't understand why this draw() function is skipped:
function main() {
if (!init()) return;
draw();
}
Added 11/6/2022
function loadTexture(url) {
return new Promise(resolve => {
const image = new Image();
image.onload = () => {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
resolve(texture);
};
image.src = url;
});
}
async function init() {
const texture = await loadTexture("./assets/texture.png");
}
init();
First off, I'm new at WebGL. I am trying to applying multiple fragment shaders(here, 2 shaders) on a single image to be rendered. I read in different articles and other stack overflow questions that we should use framebuffers (ping pong method) for this purpose but couldn't find any sample code snippets anywhere. What I understood is first I create two programs each with a different fragment shader. Then use a framebuffer object where I can use my first program (first shader) on the original image and output it to that fbo texture. Then use this output texture as the input in the second program so that both shaders are retained. This output is finally rendered on the canvas.
I tried doing the same thing , but my canvas is completely black. I am not getting any errors in the console everything seems fine but not result.
I am struck with for hours . Could anyone help me check it?
Below is the code I wrote
const canvas = document.querySelector("canvas")
const gl = canvas.getContext("webgl");
//create two programs using a createprogram function written in my code.
const programA = createProgram(gl, vertexShader, fragmentShaderA); // program using #shader1
const programB = createProgram(gl, vertexShader, fragmentShaderB);
const texFbPair = createTextureAndFramebuffer(gl); //function defined below
setAttributes(programA);
setAttributes(programB);
function setAttributes(program) {
const positionLocation = gl.getAttribLocation(program, 'position');
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, -1, 1, 1, -1,
1, 1, 1, -1, -1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 1.0,
0.0, 0.0,
1.0, 1.0,
1.0, 0.0,
1.0, 1.0,
0.0, 0.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
}
const texture = gl.createTexture();
texture.image = new Image();
texture.image.onload = function () {
handleLoadedTexture(gl, texture);
};
texture.image.crossOrigin = '';
texture.image.src = 'skogafoss_waterfall_iceland.jpg';
function handleLoadedTexture(gl, texture, callback) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
}
gl.useProgram(programA);
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair.fb);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.clearColor(0, 0, 1, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.useProgram(programB);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, texFbPair.tex);
gl.clearColor(0, 0, 0, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6)
function createTextureAndFramebuffer(gl) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
return { tex: tex, fb: fb };
}
It looks like from your code you have a mis-understanding of how attributes work. Attributes are global state in WebGL1 so these lines
setAttributes(programA);
setAttributes(programB);
Won't work. The second call to setAttributes will just change the global attributes to the second call's settings.
See this and this
The next issue is the code does not wait for the image to load so it creates an image, sets a callback for when it finishes loading, it then draws 2 things. Then later, the image finishes loading and is copied to the texture but no drawing happens after that.
The code also never allocates the actual texture in createTextureAndFramebuffer
To do that you to call gl.texImage2D
Here is some working code.
const vertexShader = `
attribute vec4 position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = position;
v_texCoord = a_texCoord;
}
`;
const fragmentShaderA = `
precision highp float;
varying vec2 v_texCoord;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texCoord);
}
`;
const fragmentShaderB = `
precision highp float;
varying vec2 v_texCoord;
uniform sampler2D tex;
void main() {
gl_FragColor = texture2D(tex, v_texCoord);
}
`;
const canvas = document.querySelector("canvas")
const gl = canvas.getContext("webgl");
//create two programs using a createprogram function written in my code.
const programA = createProgram(gl, vertexShader, fragmentShaderA); // program using #shader1
const programB = createProgram(gl, vertexShader, fragmentShaderB);
const texFbPair = createTextureAndFramebuffer(gl); //function defined below
function setAttributes(program) {
const positionLocation = gl.getAttribLocation(program, 'position');
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, -1, 1, 1, -1,
1, 1, 1, -1, -1, 1,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 1.0,
0.0, 0.0,
1.0, 1.0,
1.0, 0.0,
1.0, 1.0,
0.0, 0.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
}
const texture = gl.createTexture();
texture.image = new Image();
texture.image.onload = function () {
handleLoadedTexture(gl, texture);
};
texture.image.crossOrigin = '';
texture.image.src = 'https://i.imgur.com/ZKMnXce.png';
function handleLoadedTexture(gl, texture, callback) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
setAttributes(programA);
gl.useProgram(programA);
gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair.fb);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.clearColor(0, 0, 1, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
setAttributes(programB);
gl.useProgram(programB);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, texFbPair.tex);
gl.clearColor(0, 0, 0, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6)}
function createTextureAndFramebuffer(gl) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D,
0, // mip level
gl.RGBA, // internal format
gl.canvas.width, // width
gl.canvas.height, // height
0, // border
gl.RGBA, // format
gl.UNSIGNED_BYTE, // type
null); // data
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
return { tex: tex, fb: fb };
}
function createProgram(gl, vs, fs) {
return twgl.createProgram(gl, [vs, fs]);
}
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
Other things I noticed.
The uniforms for the samplers are never looked up (of course I don't know what your actual shaders look like, I used placeholders). As such it works because uniforms default to 0 so the programs will reference the texture bound to texture unit 0 which is the default.
I also noticed the first draw via the framebuffer sets the viewport to the size of the canvas. That's correct if the size of the attachments in the framebuffer are the size of the canvas (which I made them be when I added the call to texImage2D) but it would probably be more appropriate to record a width and height for that texture so if you change its size the code won't fail.
Finally while I moved the calls to setAttributes to the correct places it's not common to also create and fill out buffers when rendering. It's more common to create buffers at init time and set attributes at render time but I didn't want to change more code.
You might find these tutorials helpful.
To The Readers:
"The solution by gman works for the question, but my own Problem was a in a diffrent part of the Code".
I just wanted to render my Scene to a Framebuffer with a DepthComponent_texuture. And wanted to render this texture to the screen, so that i olny see the DepthComponent on my Screen. I want to get into Shadowmapping and throught this would be a good exercise.
Stuff i did:
-set read/drawBuffer(s) to gl.NONE because i dont hava a colorAttachment (probably dosent even matter).
-when creating the depth texture i tried gl.texImage2D but could not get it to work.
-When i am using a ColorAttachment isted of a DepthAttachment, i get a normal result(The color Attachment_texture just has the sceen)[obviously here i also added a DepthRenderbuffer(DEPTH24STENCTIL8)].
//creating the Framebuffer
id = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER,id);
atex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, atex);
gl.texStorage2D(gl.TEXTURE_2D,1,gl.DEPTH_COMPONENT16,width,height);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, atex, 0);
gl.drawBuffers([gl.NONE]);
gl.readBuffer(gl.NONE);
gl.bindFramebuffer(gl.FRAMEBUFFER,null);
//----------------
//In the renderLoop
//renders the scene with the pbrShader to the Framebuffer
//The FragmentShader of the PBRShader is empty cause i dont have any
//ColorAttachments and the Vertex is just transforming the position and
//passing the uvs[...]
render(scene,pbrShader,framebuffer);
//renders a texture to the screen using a very simple vertex shader and
//a Fragment Shader that just takes the texture and mapps it to the Screen
renderScreen(framebuffer.atex);
//Some more Code for clarity:
renderScene(scene,shader,fbo=null){
if(fbo!=null){
fbo.activate();
}
this.gl.fClear();
let batches = new Map();
let modals = scene.getEntitiesByType("modal");
modals.forEach(modal=>{
if(batches.has(modal.mesh)){
batches.get(modal.mesh).push(modal);
}else{
batches.set(modal.mesh,[modal]);
}
})
shader.activate().prepareScene(scene);
batches.forEach((batch,mesh)=>{
shader.prepareVAO(mesh);
batch.forEach(modal=>{
shader.prepareInstance(modal);
shader.drawVAO(mesh);
});
shader.unbindVAO(mesh);
});
shader.deactivate();
if(fbo!=null){
fbo.deactivate();
}
}
Here's working example of using depth textures in WebGL2
const vs = `#version 300 es
layout(location = 0) in vec4 a_position;
out vec2 v_texcoord;
void main() {
gl_Position = a_position;
v_texcoord = a_position.xy * 0.5 + 0.5;
}
`;
const fs = `#version 300 es
precision mediump float;
in vec2 v_texcoord;
uniform sampler2D u_sampler;
out vec4 outColor;
void main() {
vec4 color = texture(u_sampler, v_texcoord);
outColor = vec4(color.r, 0, 0, 1);
}
`;
function main() {
var gl = document.querySelector("canvas").getContext('webgl2');
if (!gl) {
return alert("no WebGL2");
}
var program = twgl.createProgram(gl, [vs, fs]);
gl.useProgram(program);
var verts = [
1, 1, 1,
-1, 1, 0,
-1, -1, -1,
1, 1, 1,
-1, -1, -1,
1, -1, 0,
];
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
// create a depth texture.
var depthTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, depthTex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
const mipLevel = 0;
const depthTexWidth = 16;
const depthTexHeight = 16;
gl.texImage2D(
gl.TEXTURE_2D, mipLevel, gl.DEPTH_COMPONENT24,
depthTexWidth, depthTexHeight, 0,
gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
// Create a framebuffer and attach the textures.
var fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D, depthTex, 0);
// use some other texture to render with while we render to the depth texture.
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA8,
1, // width
1, // height
0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));
// Turn on depth testing so we can write to the depth texture.
gl.enable(gl.DEPTH_TEST);
// note: we don't need to set u_sampler because uniforms
// default to 0 so it will use texture unit 0 which
// is the also the default active texture unit
// Render to the depth texture
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, depthTexWidth, depthTexHeight);
gl.clear(gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Now draw with the texture to the canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.bindTexture(gl.TEXTURE_2D, depthTex);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
I am trying to load an image file into my webGL code and create a texture out of it but while loading the image i get the error:
Uncaught TypeError: Failed to execute 'texImage2D' on 'WebGLRenderingContext':
parameter 9 is not of type 'ArrayBufferView'.
at Image. (app.js:13)
var image = new Image();
image.src = "./js-logo.png";
image.addEventListener("load", function(){
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 10, 10, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.generateMipmap(gl.TEXTURE_2D);
});
See WebGL Specification - 5.14 The WebGL context:
[...]
void texImage2D(GLenum target, GLint level, GLint internalformat,
GLsizei width, GLsizei height, GLint border, GLenum format,
GLenum type, [AllowShared] ArrayBufferView? pixels);
void texImage2D(GLenum target, GLint level, GLint internalformat,
GLenum format, GLenum type, TexImageSource source); // May throw DOMException
Since image is image is a TexImageSource it has to be
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 10, 10, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
Note, the size of the bitmap is encode int the image object.
Further you should set the texture filtering parameters:
var image = new Image();
image.src = "./js-logo.png";
image.addEventListener("load", function(){
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 10, 10, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR );
gl.generateMipmap(gl.TEXTURE_2D);
});
I'm trying to use a framebuffer to render to a cubemap, but I'm getting an "FRAMEBUFFER_INCOMPLETE_ATTACHMENT" error. I can used the code with a 2d texture, with the type set to FLOAT or UNSIGNED_BYTE. Is there some mistake in the way I've set-up the texture cube parameters or attached in it in this code:
this.inscatterTexture_ = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.inscatterTexture_);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
for (let i = 0; i < 6; i++) {
// Create framebuffer
this.inscatterFrameBuffers_[i] = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.inscatterFrameBuffers_[i]);
// Create and attach depth buffer
this.inscatterDepthBuffers_[i] = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.inscatterDepthBuffers_[i]);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, INSCATTER_RESOLUTION, INSCATTER_RESOLUTION);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.inscatterDepthBuffers_[i]);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
// Attach one face of cube map
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, INSCATTER_RESOLUTION, INSCATTER_RESOLUTION, 0, gl.RGBA, gl.FLOAT, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.inscatterTexture_, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
let status_code = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
console.log("Inscatter frame buffer, " + i + ", is not complete: " + FramebufferStatus[status_code]);
}
this.CreateInscatterTexture(gl, i);
}
Apparently you need to create all faces of the cubemap first.
"use strict";
function log() {
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(Array.prototype.join.call(arguments, " ")));
document.body.appendChild(pre);
}
function glEnumToString(gl, value) {
for(var key in gl) {
if (gl[key] === value) {
return key;
}
}
return "0x" + value.toString(16);
}
var INSCATTER_RESOLUTION = 64;
var gl = document.createElement("canvas").getContext("webgl");
var ext = gl.getExtension("OES_texture_float");
if (!ext) { log("need OES_texture_float"); }
ext = gl.getExtension("OES_texture_float_linear");
if (!ext) { log("need OES_texture_float_linear"); }
var o = {};
(function() {
this.inscatterFrameBuffers_ = [];
this.inscatterDepthBuffers_ = [];
this.inscatterTexture_ = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.inscatterTexture_);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
for (let i = 0; i < 6; i++) {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, INSCATTER_RESOLUTION, INSCATTER_RESOLUTION, 0, gl.RGBA, gl.FLOAT, null);
}
for (let i = 0; i < 6; i++) {
// Create framebuffer
this.inscatterFrameBuffers_[i] = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.inscatterFrameBuffers_[i]);
// Create and attach depth buffer
this.inscatterDepthBuffers_[i] = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.inscatterDepthBuffers_[i]);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, INSCATTER_RESOLUTION, INSCATTER_RESOLUTION);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.inscatterDepthBuffers_[i]);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
// Attach one face of cube map
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.inscatterTexture_, 0);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
let status_code = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
log("Inscatter frame buffer, " + i + ", is not complete: " + glEnumToString(gl, status_code));
} else {
log("success");
}
}
}).call(o);
This kinds of seems like a driver bug. I know that at least in the past Nvidia required a texture to be renderable before it would give FRAMEBUFFER_COMPLETE even though you might not yet be using the texture for rendering. For example if you make a texture with no mips and don't set its filtering it would fail.