I am working on a WebGL project that requires blending of 100 pictures. However, there is significant data loss in this process.
What I mean by that is, for example, I did a test to draw the same picture 100 times with each of them setting alpha value 1/100. In principle I should have got the same picture as itself but I have not.
I suspect that has something to do with framebuffer which only supports 8 bit unsign int array thus each time the picture gets drawn, its alpha value is truncated. I was wondering if I can use floating point texture with OES-texture-float extension and rendering to texture to solve the issue.
First I set up a framebuffer to render my 100 pictures to (after enabling the "OES-texture-float" extension). In particular, texImage2D takes the parameter gl.FlOAT instead of gl.UNSIGNED_BYTE. Finally I draw rttTexture as the final result to the display.
The issue is that when I used gl.UNSIGNED_BYTE it outputs a compromised result as I described before. However, when I used gl.FlOAT it outputs nothing. Is this the right process for me to solve this issue? Where might the problem be? (I am sure that the extension is enabled)
rttFramebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
rttFramebuffer.width = 512;
rttFramebuffer.height = 512;
rttTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, rttTexture);
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);
gl.generateMipmap(gl.TEXTURE_2D);
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, rttFramebuffer.width rttFramebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, rttFramebuffer.width, rttFramebuffer.height, 0, gl.RGBA, gl.FLOAT, null);
var renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, rttFramebuffer.width, rttFramebuffer.height);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, rttTexture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
drawScene();
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
drawFinalScene();
Related
i'm trying to load image on webgl by texture, the detail steps is:
after loaded image ,create positions and bind positionBuffer:
...
const positions = [ // here width is viewport width, and height calculted by height = naturalHeight / naturalWidth * width;
-width / 2, -height / 2, 0.0,
width / 2, -height / 2, 0.0,
width / 2, height / 2, 0.0,
-width / 2, height / 2, 0.0,
]
...
create textureCoordinates and bind textureCoordBuffer
create indices( define which vertexes combines a plane ) and bind indexBuffer
create projectionMatrix ,modelViewMatrix, pull out the positions from the positionbuffer into the vertexPosition attribute, pull out the texture coordinates from the texture coordinate buffer into the textureCoord attribute
pass projectionMatrix,modelViewMatrix by gl.uniformMatrix4fv
bindTexture:
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, image);
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.LINEAR );
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,gl.LINEAR );
last step is active texture0,bindTexture,bound the texture to texture unit 0, and gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
After these steps, the result on the screen just looks compressed, the image becomes fuzzy and full of sawtooth.
What could i do to solve this problem ?
I try to perform MSAA on a framebuffer, And in the standalone version where i draw a cube to the framebuffer and blit that framebuffer to the canvas it works like a charm:
var gl = canvas.getContext("webgl2", {
antialias: false
});
const frambuffer = gl.createFramebuffer();
const renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, this.width, this.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer);
.. Prepare scene
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
.. Draw scene
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, frambuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
gl.blitFramebuffer( 0, 0, canvas.width, canvas.height,
0, 0, canvas.width, canvas.height,
gl.COLOR_BUFFER_BIT, gl.LINEAR);
But when i do this in my engine with a deferred pipeline the blit is performed but the MultiSample (MSAA) not. The difference i can think of is that i am there writing an image drawn to a quad to the framebuffer and in the working example a cube.
as requested,In the case it is not working the setup is like this:
var gl = canvas.getContext("webgl2", {
antialias: false
});
.. Load resources ..
.. Prepare renderpasses ..
shadow_depth for every light
deferred scene
ssao
shadow for first light
convolution on ssao and shadow
convolution
uber for every light
tonemap
msaa
..
.. draw renderpasses ..
deferred scene
ssao
shadow for first light
convolution on ssao and shadow
convolution
uber for every light
tonemap
...
const frambuffer = gl.createFramebuffer();
const renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, gl.getParameter(gl.MAX_SAMPLES), gl.RGBA8, this.width, this.height);
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer);
gl.bindFramebuffer(gl.FRAMEBUFFER, frambuffer);
..draw tonemap of scene to quad
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, frambuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.clearBufferfv(gl.COLOR, 0, [1.0, 1.0, 1.0, 1.0]);
gl.blitFramebuffer( 0, 0, canvas.width, canvas.height,
0, 0, canvas.width, canvas.height,
gl.COLOR_BUFFER_BIT, gl.LINEAR);
renderbufferStorageMultisample needs to be applied only for the framebuffer object that has the initial 3D content. When doing post-processing, multisampling does not have an effect because only 1 or 2 triangles being rasterized and they span the entire viewport.
I run into this error when I set a non-power of to image on webGL:
Error: WebGL warning: drawArrays: TEXTURE_2D at unit 0 is incomplete:
Mipmapping requires power-of-two sizes.
Note, I am currently learning webGL. I have notice that when I use a Power-of-Two image every thing works fine.
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, this);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
All I want to do is learn to load all kinds of images on webgl
WebGL1 has limits on non-power-of-2 textures. They can't have mips and they can't repeat. So, to use a non-power-of-2 texture you have to set these texture parameters
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);
You can also use gl.NEAREST
Sometimes you can use code like this
...
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// Check if the image is a power of 2 in both dimensions.
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
// Yes, it's a power of 2. Generate mips.
gl.generateMipmap(gl.TEXTURE_2D);
} else {
// No, it's not a power of 2. Turn off mips and set wrapping to clamp to edge
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.LINEAR);
}
...
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}
See this article
I have a one question about WebGLRenderingContext's texImage2D and texSubImage2D function.
What I trying to create is something similar like loading image by tile by tile.
Here is my first function. It's working properly while it's first load. But whenever loading second time using same function but tiled background.
It failed. Look at the code below
// This is first load code which is working fine
gl.bindTexture(gl.TEXTURE_2D, handle)
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
gl.bindTexture(gl.TEXTURE_2D, handle)
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image)
// This is second time using same but one different coding section [image data from canvas]. But it's not working.
gl.bindTexture(gl.TEXTURE_2D, handle)
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, [image data from canvas])
gl.bindTexture(gl.TEXTURE_2D, handle)
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image)
Have you ever face the problem similar like me. Then please guide me to the light. Much appreciate it.
UPD
on('start', function (data) {
gl.bindTexture(gl.TEXTURE_2D, handle)
// gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, data.width, data.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, myCanvasData)
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, data.width, data.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(myCanvasData.buffer))
}).on('progress', function (ev) {
var x = ev.position[0]
var y = texHeight - ev.position[1] - ev.image.height
// now blit the intermediate image
gl.bindTexture(gl.TEXTURE_2D, handle)
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, ev.image)
}).on('complete', function () {
})
Console warning image
If I've understood your question correctly, you're probably using texImage2D wrong. The thing is, for ImageData signature of the function is a bit different (it's the same as for am image):
void texImage2D(GLenum target, GLint level, GLint internalformat, GLenum format, GLenum type, TexImageSource? source),
where TexImageSource can be an ImageData object. So you just need to pass it, without explicitly passing width and height of the image:
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
gl.RGBA,
gl.UNSIGNED_BYTE,
canvas.getImageData(/* ... */)
);
You need to use like texImage2D without explicitly passing width and height. Because i think texSubImage2D function width and height requires thought
on('start', function (data) {
gl.bindTexture(gl.TEXTURE_2D, handle)
// gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, data.width, data.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, myCanvasData)
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, data.width, data.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(myCanvasData.buffer))
}).on('progress', function (ev) {
var x = ev.position[0]
var y = texHeight - ev.position[1] - ev.image.height
// now blit the intermediate image
gl.bindTexture(gl.TEXTURE_2D, handle)
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, ev.image)
}).on('complete', function () {
})
Console warning image
recently I have been suffering from unpack 6 images from single environment texture for rendering reflection pass.
SOURCE IMG
It turns out in webGl it is difficult to use texImage2D in this kind of job, since texImage2D doesn't provide xoffset, neither yoffset. Granted copyTexImage2D and texSubImage2D did provide offset parameters, however, by using those two functions never run for me
Finally, I found the solution by render the image into canvas then pass the canvas to texImage2D.
RENDER RESULT
But I got annoying texture seam on the screen. How did that happen and how can I fix it? Thanks guys.
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
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);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var myCanvas = document.createElement("canvas");
myCanvas.width = 512;
myCanvas.height = 512;
var myCanvasContext = myCanvas.getContext("2d"); // Get canvas 2d context
myCanvasContext.drawImage(_this._data.image, 1024, 512, 512,512,0,0,512,512);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, myCanvas);
myCanvasContext.drawImage(_this._data.image, 0, 512, 512,512,0,0,512,512);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, myCanvas);
myCanvasContext.drawImage(_this._data.image, 512, 1024, 512,512,0,0,512,512);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, myCanvas);
myCanvasContext.drawImage(_this._data.image, 512,0, 512,512, 0,0,512,512);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, myCanvas);
myCanvasContext.drawImage(_this._data.image, 512, 512,512,512,0,0,512,512);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, myCanvas);
myCanvasContext.drawImage(_this._data.image, 1536, 512,512,512,0,0,512,512);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, myCanvas);
_this._data.image is the environment image; and each texture block is 512X512.