WEBGL - How to display an image? - javascript

i'm looking to just simply display an image on the canvas at x and y co-ordinates using WEBGL but have no clue how to do it. do i need to include shaders and all that stuff? i've seen code to display images but they are very bulky. I do not wish to use a framework. If possible could you comment and explain what the important sections do? I will be using WEBGL for a 2d tile based game.
thankyou for your time

Yes, you need a vertex and fragment shader, but they can be relatively simple. I'd recommend to start from the Mozilla example, as suggested by Ido, and after you got it running, remove the 3D aspect. In particular, you don't need the uMVPMatrix and uPmatrix, and your coordinate array can be 2D. For the vertex shader, that means:
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
varying highp vec2 vTextureCoord;
void main(void) {
gl_Position = vec4(aVertexPosition, 0.0, 1.0);
vTextureCoord = aTextureCoord;
}

Related

How to find current output pixel position in clip-space coordinates in WebGL?

So, in my vertex shader I'm doing something like this:
void main() {
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
}
But now I'm in my fragment shader and I would like to access that location. That is, I'm looking for a variable to access the current location of the pixel being drawn, but in clip-space coordinates. The reason for this is that I am looking to blend that pixel with a pixel at the same location in a texture.
It looks like gl_FragCoord is similar to what I want, but it's in window-space. I think I would be able to convert it like so:
vec2 what_i_want = vec2(gl_FragCoord.x / 1920., gl_FragCoord.y / 1080.);
But then I need to know my canvas size. I could pass in the canvas size as a uniform, but that seems like a bit much. Is there no way to just access this value somehow in the fragment shader?
You are on the right track. You must set the size of the viewport (canvas) in a uniform variable. There is no other option. That's what everyone does in WebGL as in OpenGL.
uniform vec2 u_resolution;
void main()
{
vec2 uv = gl_FragCoord.xy / u_resolution;
// [...]
}

check if webgl texture is loaded in fragment shader

i am writing a webgl program with texturing.
As long as the image isn´t loaded, the texture2D-function returns a vec4(0.0, 0.0, 0.0, 1.0). So all objects are black.
So i would like to check, if my sampler2D is available.
I have already tried:
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
vec4 color = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
if(color.r == 0.0 && color.g == 0.0 && color.b == 0.0)
color = vec4(1.0, 1.0, 1.0, 1.0);
gl_FragColor = color;
}
</script>
But of course this doesn´t make sense, because the texture could be black.
Can anybody help me? How can I check, whether my texture image is already loaded in the fragment shader?
You can't really check that in WebGL.
Solutions:
Don't render until the texture is loaded
Use a 1x1 pixel texture to start, fill it in with the image
once it's loaded. See this answer
Pass in more info to the shader like uniform bool textureLoaded.
Me, I always pick #2 because it means the app runs immediately and the textures get filled in as they download.
I'd provide new uniform which will store data whether texture is loaded or not.
Or you can write 2 shaders with/without texture and select proper one before rendering.

Calculating accurate UV coordinates in a spritesheet

I am working with a WebGL website.
I have a canvas with a texture at 256x256 that I use to render to WebGL.
On this canvas I have rendered several images packed together with 1px spacing between, using regular canvas rendering.
I use a 1x1 rectangle (scaled with the world matrix) to render the images in batches. I.e.: I set up the entire render state, then change the UV as a uniform to the shader. It's a spritesheet of icons.
The shader I use to render it is
precision highp float;
attribute vec3 vertexPosition;
attribute vec2 textureCoordinate;
uniform mat4 worldMatrix;
uniform mat4 projectionMatrix;
uniform vec4 actualUV;
uniform float cacheSize;
varying vec2 fragCoord;
vec2 scaleVec;
void main(void) {
scaleVec = vec2(cacheSize,cacheSize);
gl_Position = projectionMatrix * worldMatrix * vec4(vertexPosition, 1.0);
fragCoord = textureCoordinate * actualUV.zw;
fragCoord = fragCoord + actualUV.xy;
fragCoord = fragCoord * scaleVec;
}
The values I use are
actualUV={x:50, y:50, z:19:, w:19}; // for example
cacheSize = 256;
Which should render 19x19 pixels at 50,50 on the texture into a rectangle on the screen 19x19 size. And it does, almost.
The image is slightly off. It's blurry and when I set the MAG_FILTER to NEAREST I get a sharper image, but it is sometimes off by one pixel, or worse, a half pixel causing some (very minor but noticable) stretching. If I add a slight offset to correct this other images rendered in the same way are off in the other direction. I cannot seem to figure it out. It seems like it's a issue with the floating point calculation not being exact, but I cannot figure out where.
Try re-adjusting your coordinate system so that the UVs passed are within [0-1] range and get rid of your scaling factor. This can also be a pre multiplied alpha problem, try use gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); with gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); instead. If you are using this for tiles, then you need to pad the borders with actual pixels. Need to pad more if you need anisotropic filtering.
Also, try to use a quad with dimensions equal to the image (portion of spritesheet) instead of a 1x1 quad if all above fails.

How to send multiple parameters to the vertex shader on WebGL?

I'm following a tutorial on OpenGL (because WebGL tutorials are rare), where it uses the following syntax for multiple parameteres:
#version 330
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
smooth out vec4 theColor;
void main()
{
gl_Position = position;
theColor = color;
}
But the layout (location = #) syntax doesn't work on WebGL. What is the substitute for this?
There are a number of things wrong with this shader if you intend to use it in WebGL.
For starters, WebGL is based on OpenGL ES 2.0, which uses a version of GLSL derived from 120.
Your #version directive is invalid for WebGL; you cannot use in, out, or smooth for vertex attributes or varying variables; there is no layout qualifier.
This will get you part of the way to fixing your shader:
#version 100
attribute vec4 position;
attribute vec4 color;
varying vec4 theColor;
void main()
{
gl_Position = position;
theColor = color;
}
But you will also need to bind the attribute locations for position and color in your code (before linking your shaders - see glBindAttribLocation (...)). If you are having difficulty finding tutorials for WebGL / ESSL, you can re-use many OpenGL tutorials that were written for GLSL version 120 or older.
You can read the offical specification for OpenGL ES 2.0's GLSL (ESSL) here. At the very least, have a look at the introductory section because it contains a lot of useful information.

Is it possible to apply fog from a point in a three.js scene that is independent of the camera?

For example, given a terrain with an avatar on it with a camera far away overhead: is it possible to render the fog so that the avatar remains perfectly unfogged while the terrain around the avatar fades into the fog?
Sure, though as far as I know, you'll have to make your own shader rather than using the ones provided with three.js. There may be a way to customize them in this way, but if there is, I'm not familiar with it.
Check out this answer on doing fog as distance from the camera. The idea, as explained there, is to pass the camera position in as a uniform to the shader, then in the vertex shader on all your objects, you find the distance from the camera position to the vertex you're transforming. You then pass that distance along as a varying to the fragment shader, and you can figure out the distance per pixel, which you use to mix between a fogged color and the object's regular color. You can see that in this example from the OpenGL ES 2.0 Programming guide.
To change it to be based on distance from the character is simple: you just pass in the character position as the uniform that you're calculating distance from instead of the camera position (in that sample code, you would replace u_eyePos with something like u_characterPos and maybe change the varying from v_eyeDist to v_characterDist). Except for any name changes, the fragment shader can be exactly the same.
So, something like this (WARNING: NOT TESTED. you're going to have to fix this up to have three.js happy with using it. There are a ton of example of that, though, like this one):
vertex shader:
uniform mat4 matViewProjection;
uniform mat4 matView;
uniform vec4 u_characterPos;
attribute vec4 rm_Vertex;
attribute vec2 rm_TexCoord0;
varying vec2 v_texCoord;
varying float v_characterDist;
void main() {
// Transform vertex to view-space
vec4 vViewPos = matView * rm_Vertex;
// Compute the distance to character
v_characterDist = length(vViewPos - u_characterPos);
gl_Position = matViewProjection * rm_Vertex;
v_texCoord = rm_TexCoord0.xy;
}
fragment shader:
precision mediump float;
uniform vec4 u_fogColor;
uniform float u_fogMaxDist;
uniform float u_fogMinDist;
uniform sampler2D baseMap;
varying vec2 v_texCoord;
varying float v_characterDist;
float computeLinearFogFactor() {
float factor;
// Compute linear fog equation
factor = (u_fogMaxDist - v_characterDist) /
(u_fogMaxDist - u_fogMinDist );
// Clamp in the [0,1] range
factor = clamp(factor, 0.0, 1.0);
return factor;
}
void main() {
float fogFactor = computeLinearFogFactor();
vec4 fogColor = fogFactor * u_fogColor;
vec4 baseColor = texture2D(baseMap, v_texCoord);
// Compute final color as a lerp with fog factor
gl_FragColor = baseColor * fogFactor +
fogColor * (1.0 - fogFactor);
}

Categories