Lighting not dynamically changing on objects when moved - opengl-es

I'm having trouble with my lighting source and objects in my webGL app. In my "drawScene" function, i load the view port, clear the view, then render my light. After i identify my matrix and render my VBOs (with pushing and poping the matrix).
What happens then once i load my app, the light source is correct, its ontop of the object
I then move the light source to the left, and the lighting displays correctly on the object as it should
Then here is the problem. I move the object to the left, past the light source but the lighting on the object does not move to the right side of the object
If i were to move the lighting all the way to the left, it only changes on the object once the light passes the initial starting position (0, 0).
I thought it was a push and poping matrix issue, but i've corrected the architecture of the code. Light renders first, then the matrix is called, and then the object. It may be a issue with my shaders but i can not tell... Here is what my shaders look like. Think anyone could help me out?
<script id="shader-fs" type="x-shader/x-fragment"> // Textured, lit, normal mapped frag shader precision mediump float;
// uniforms from app
uniform sampler2D samplerD; // diffuse texture map
uniform sampler2D samplerN; // normal texture map
uniform vec3 uLightColor; // directional light color
uniform vec3 uAmbientColor; // ambient light color
// interpolated values from vertex shader
varying vec2 vTextureCoord;
varying vec3 vLightDir;
void main()
{
// get the color values from the texture and normalmap
vec4 clrDiffuse = texture2D(samplerD, vTextureCoord);
vec3 clrNormal = texture2D(samplerN, vTextureCoord).rgb;
// scale & normalize the normalmap color to get a normal vector for this texel
vec3 normal = normalize(clrNormal * 2.0 - 1.0);
// Calc normal dot lightdir to get directional lighting value for this texel.
// Clamp negative values to 0.
vec3 litDirColor = uLightColor * max(dot(normal, vLightDir), 0.0);
// add ambient light, then multiply result by diffuse tex color for final color
vec3 finalColor = (uAmbientColor + litDirColor) * clrDiffuse.rgb;
// finally apply alpha of texture for the final color to render
gl_FragColor = vec4(finalColor, clrDiffuse.a);
}
</script>
<script id="shader-vs" type="x-shader/x-vertex"> // Textured, lit, normal mapped vert shader precision mediump float;
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord; // Texture & normal map coords
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform vec3 uLightDir; // Application can set desired light direction
varying vec2 vTextureCoord; // Passed through to frag shader
varying vec3 vLightDir; // Compute transformed light dir for frag shader
void main(void)
{
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
vLightDir = uLightDir;
vLightDir = normalize(vLightDir);
}
</script>
#MaticOblak
function webGLStart() { ... this.light = new Light(); ...
function Light() { ... this.create(); ...
Light.prototype.create = function() {
gl.uniform3fv(shader.prog.lightDir, new Float32Array([0.0, 0.0, 1.0, 1.0]));
gl.uniform3fv(shader.prog.lightColor, new Float32Array([0.8, 0.8, 0.8]));
gl.uniform3fv(shader.prog.ambientColor, new Float32Array([0.2, 0.2, 0.2]));
}
function drawScene() {
...
this.light.render();
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [-player.getPosition()[0], -
player.getPosition()[1], 0.0]);
mat4.translate(mvMatrix, [0, 0, -20.0]);
this.world.render();
this.player.render();
Light.prototype.render= function() {
gl.uniform3f(shader.prog.lightDir,
this.position[0],this.position[1],this.position[2] );
}

Related

Three.js fragment shader with ambient light intensity

I have a Three.js shader material that chromakeys a video texture so that the green becomes transparent. That part works fine.
Now i am trying to modify it so that it is affected by the intensity of the ambient light, basically what i want is that when the ambient light's intensity is lower, the video playing becomes darker.
On images I can do that fine by simply adding a Standard Material so i've tried adding two separate materials to the video (the chromakey shader material and a standard one) but that didn't help.
So I started doing some research and digging into the code of the chromakey shader (which was not written by me) and i made the following changes:
I've merged the original uniforms with the ones from THREE.UniformsLib["lights"]
I've enabled the lights in the shader material's parameters
Now the question is, how do I access the ambient light's intensity value (which is constantly updating by the way) inside the fragment shader, and how do I make the pixels darker depending on the intensity value (which is between 0 and 1).
Shader Material
var uniforms = THREE.UniformsUtils.clone(THREE.UniformsLib["lights"]);
uniforms['color'] = { type: 'c', value: data.color };
uniforms['texture'] = { type: 't', value: videoTexture };
this.material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: this.vertexShader,
fragmentShader: this.fragmentShader,
lights: true
});
Vertex Shader
varying vec2 vUv;
void main(void)
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
Fragment Shader
uniform sampler2D texture;
uniform vec3 color;
varying vec2 vUv;
void main(void)
{
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor, a);
}
I basically need to modify tColor according to the light's intensity but like I said, I have no idea how to access that value and how to darken/brighten the color according to it.
Adding brightness control to you fragment shader
If its just a simple brightness control you can multiply the frag color by a scalar.
Example frag shader
uniform sampler2D texture;
uniform vec3 color;
uniform float brightness; // added uniform to control brightness
varying vec2 vUv;
void main(void) {
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor * brightness, a); // scale brightness of rgb channels
}
Then in Javascript code to support the new uniform
const BRIGHTNESS_MAX = 1; // default value. MAX brightness DO not change this
// DO NOT CHANGE this value to set upper limit
// Use the const LUX_MAX (see below) to set upper limit
const BRIGHTNESS_MIN = 0.7; // The darkest you want A value of 0 is black
// add brightness The same uniforms object as you
// got with uniforms = THREE.UniformsUtils.clone(THREE.UniformsLib["lights"]);
uniforms.brightness = {value: BRIGHTNESS_MAX}; // default to max
To change the value of the uniform
uniforms.brightness.value = brightness; // set the new value
From Three Docs
"All uniforms values can be changed freely (e.g. colors, textures, opacity, etc), values are sent to the shader every frame."
So that is all that is needed to add the brightness control.
Using Ambient sensor
I will assume you have access to the sensor value. The sensor holds the light level as an absolute value LUX 10 is dark ~707 is normal and 10000 plus is bright.
You will have to calibrate the sensor reading by changing what LUX corresponds to BRIGHTNESS_MAX and setting the BRIGHTNESS_MIN to the darkest you want the image to become.
As the the scaling and dynamic range of the light sensor and display device are very different the following function makes the assumption the MAX_LUX and white on the rendered image are the same brightness
The following function will convert from a LUX value to a brightness value
const MAX_LUX = 5000; // This LUX value and above will set brightness to max
function LUX2Brightness(lux) {
if (lux >= MAX_LUX) { return BRIGHTNESS_MAX }
const MIN = (BRIGHTNESS_MIN ** 2.2) * MAX_LUX; // do not manually set this value
// Set BRIGHTNESS_MIN to control
// low light level
if (lux <= MIN) { return BRIGHTNESS_MIN }
return (lux ** (1 / 2.2)) / (MAX_LUX ** (1 / 2.2));
}
To use the above function with the shader
// luminosity is value from ambient light sensor event
uniforms.brightness.value = LUX2Brightness(luminosity);
The assumption is that you set MAX_LUX to the actual LUX output of the all white rendered image (best of luck with that).
IMPORTANT!!
The is no absolute solution to levels.
Human vision is adaptive. How you calibrate the min and max will change depending on how your eyes have adapted to the current light levels, the current brightness, color, (and more) setting on the device displaying the rendered content, the current setting of the camera, exposure, white balance (and so on), your personal artist preferences.
All of these things are usually set automatically so any setting that looks good now may not be what is desired in the morning, or when you come back from a coffee break.
All the code
Fragment shader
uniform sampler2D texture;
uniform vec3 color;
uniform float brightness;
varying vec2 vUv;
void main(void) {
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor * brightness, a);
}
JavaScript settup code
const BRIGHTNESS_MAX = 1; // Don't change this value
const BRIGHTNESS_MIN = 0.7;
const MAX_LUX = 2000;
uniforms.brightness = {value: BRIGHTNESS_MAX};
function LUX2Brightness(lux) {
if (lux >= MAX_LUX) { return BRIGHTNESS_MAX }
const MIN = (BRIGHTNESS_MIN ** 2.2) * MAX_LUX;
if (lux <= MIN) { return BRIGHTNESS_MIN }
return (lux ** (1 / 2.2)) / (MAX_LUX ** (1 / 2.2));
}
Sensor reading
Put following line in sensor event. Eg the "devicelight" event listener.
uniforms.brightness.value = LUX2Brightness(event.value);

Jittering vertices

I'm trying to get rid of spatial jitter. To be more precise, I'm trying to replicate Three.js behavior concerning matrix manipulation.
My current rendering pipeline (WebGL with m4.js)
There is a scene object, a camera and a mesh. The mesh has its position (mesh.position) set to position and the camera is floating somewhere near it.
Vertices have positions relative to mesh.position:
new Float32Array([
-5, 0, -5,
5, 0, -5,
0, 0, 5
])
Important part of render loop:
const position = {x: 6428439.8443510765, y: 0, z: 4039717.5286310893}; // mesh is located there
camera.updateMatrixWorld();
camera.updateMatrixWorldInverse();
let modelViewMatrix = m4.multiply(camera.matrixWorldInverse, mesh.matrixWorld);
material.uniforms.projectionMatrix = {type: 'Matrix4fv', value: camera.projectionMatrix};
material.uniforms.modelViewMatrix = {type: 'Matrix4fv', value: modelViewMatrix};
material.use();
mesh.draw(material);
Vertex shader:
#version 300 es
precision highp float;
in vec3 position;
in vec3 color;
out vec3 vColor;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main() {
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Fragment shader:
#version 300 es
precision highp float;
out vec4 FragColor;
in vec3 vColor;
uniform float uSample;
void main() {
FragColor = vec4(vColor * uSample, 1);
}
Result:
When moving or rotating the camera around the mesh spatial jitter effect is observed, which is not the expected behavior.
I've implemented the same scene using Three.js, and as expected you can see no jitter while moving vertices or camera: Codepen link. Three.js must work exactly the same as my implemetation, but obviously I'm missing something.
It turns out that m4.js which is a part of webgl-3d-math library used on webgl2fundamentals.org utilizes Float32Array objects for storing matrices. Maybe this approach positively affects performance, but it also causes some confusion as JavaScript's Number uses 64-bit floats.

How to texture non-unwrapped model using a cubemap

I have lots of models that ain't unwrapped (they don't have UV coordinates). They are quite complex to unwrap them. Thus, I decided to texture them using a seamless cubemap:
[VERT]
attribute vec4 a_position;
varying vec3 texCoord;
uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
...
void main()
{
gl_Position = u_projTrans * u_worldTrans * a_position;
texCoord = vec3(a_position);
}
[FRAG]
varying vec3 texCoord;
uniform samplerCube u_cubemapTex;
void main()
{
gl_FragColor = textureCube(u_cubemapTex, texCoord);
}
It works, but the result is quite weird due to texturing depends on the vertices position. If my model is more complex than a cube or sphere, I see visible seams and low resolution of the texture on some parts of the object.
Reflection is mapped good on the model, but it has a mirror effect.
Reflection:
[VERT]
attribute vec3 a_normal;
varying vec3 v_reflection;
uniform mat4 u_matViewInverseTranspose;
uniform vec3 u_cameraPos;
...
void main()
{
mat3 normalMatrix = mat3(u_matViewInverseTranspose);
vec3 n = normalize(normalMatrix * a_normal);
//calculate reflection
vec3 vView = a_position.xyz - u_cameraPos.xyz;
v_reflection = reflect(vView, n);
...
}
How to implement something like a reflection, but with “sticky” effect, which means that it’s as if the texture is attached to a certain vertex (not moving). Each side of the model must display its own side of the cubemap, and as a result it should look like a common 2D texturing. Any advice will be appreciated.
UPDATE 1
I summed up all comments and decided to calculate cubemap UV. Since I use LibGDX, some names may differ from OpenGL ones.
Shader class:
public class CubemapUVShader implements com.badlogic.gdx.graphics.g3d.Shader {
ShaderProgram program;
Camera camera;
RenderContext context;
Matrix4 viewInvTraMatrix, viewInv;
Texture texture;
Cubemap cubemapTex;
...
#Override
public void begin(Camera camera, RenderContext context) {
this.camera = camera;
this.context = context;
program.begin();
program.setUniformMatrix("u_matProj", camera.projection);
program.setUniformMatrix("u_matView", camera.view);
cubemapTex.bind(1);
program.setUniformi("u_textureCubemap", 1);
texture.bind(0);
program.setUniformi("u_texture", 0);
context.setDepthTest(GL20.GL_LEQUAL);
context.setCullFace(GL20.GL_BACK);
}
#Override
public void render(Renderable renderable) {
program.setUniformMatrix("u_matModel", renderable.worldTransform);
viewInvTraMatrix.set(camera.view);
viewInvTraMatrix.mul(renderable.worldTransform);
program.setUniformMatrix("u_matModelView", viewInvTraMatrix);
viewInvTraMatrix.inv();
viewInvTraMatrix.tra();
program.setUniformMatrix("u_matViewInverseTranspose", viewInvTraMatrix);
renderable.meshPart.render(program);
}
...
}
Vertex:
attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec3 a_normal;
attribute vec3 a_tangent;
attribute vec3 a_binormal;
varying vec2 v_texCoord;
varying vec3 v_cubeMapUV;
uniform mat4 u_matProj;
uniform mat4 u_matView;
uniform mat4 u_matModel;
uniform mat4 u_matViewInverseTranspose;
uniform mat4 u_matModelView;
void main()
{
gl_Position = u_matProj * u_matView * u_matModel * a_position;
v_texCoord = a_texCoord0;
//CALCULATE CUBEMAP UV (WRONG!)
//I decided that tm_l2g mentioned in comments is u_matView * u_matModel
v_cubeMapUV = vec3(u_matView * u_matModel * vec4(a_normal, 0.0));
/*
mat3 normalMatrix = mat3(u_matViewInverseTranspose);
vec3 t = normalize(normalMatrix * a_tangent);
vec3 b = normalize(normalMatrix * a_binormal);
vec3 n = normalize(normalMatrix * a_normal);
*/
}
Fragment:
varying vec2 v_texCoord;
varying vec3 v_cubeMapUV;
uniform sampler2D u_texture;
uniform samplerCube u_textureCubemap;
void main()
{
vec3 cubeMapUV = normalize(v_cubeMapUV);
vec4 diffuse = textureCube(u_textureCubemap, cubeMapUV);
gl_FragColor.rgb = diffuse;
}
The result is completely wrong:
I expect something like that:
UPDATE 2
The texture looks stretched on the sides and distorted in some places if I use vertices position as a cubemap coordinates in the vertex shader:
v_cubeMapUV = a_position.xyz;
I uploaded euro.blend, euro.obj and cubemap files to review.
that code works only for meshes that are centered around (0,0,0) if that is not the case or even if (0,0,0) is not inside the mesh then artifacts occur...
I would start with computing BBOX BBOXmin(x0,y0,z0),BBOXmax(x1,y1,z1) of your mesh and translate the position used for texture coordinate so its centered around it:
center = 0.5*(BBOXmin+BBOXmax);
texCoord = vec3(a_position-center);
However non uniform vertex density would still lead to texture scaling artifacts especially if BBOX sides sizes differs too much. Rescaling it to cube would help:
vec3 center = 0.5*(BBOXmin+BBOXmax); // center of BBOX
vec3 size = BBOXmax-BBOXmin; // size of BBOX
vec3 r = a_position-center; // position centered around center of BBOX
r.x/=size.x; // rescale it to cube BBOX
r.y/=size.y;
r.z/=size.z;
texCoord = r;
Again if the center of BBOX is not inside mesh then this would not work ...
The reflection part is not clear to me do you got some images/screenshots ?
[Edit1] simple example
I see it like this (without the center offsetting and aspect ratio corrections mentioned above):
[Vertex]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_g2s;
layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;
out smooth vec4 pixel_col;
out smooth vec3 pixel_txr;
//------------------------------------------------------------------
void main(void)
{
pixel_col=col;
pixel_txr=(tm_l2g*vec4(pos,0.0)).xyz;
gl_Position=tm_g2s*tm_l2g*vec4(pos,1.0);
}
//------------------------------------------------------------------
[Fragment]
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec4 pixel_col;
in smooth vec3 pixel_txr;
uniform samplerCube txr_skybox;
out layout(location=0) vec4 frag_col;
//------------------------------------------------------------------
void main(void)
{
frag_col=texture(txr_skybox,pixel_txr);
}
//------------------------------------------------------------------
And here preview:
The white torus in first few frames are using fixed function and the rest is using shaders. As you can see the only input I use is the vertex position,color and transform matrices tm_l2g which converts from mesh coordinates to global world and tm_g2s which holds the perspective projection...
As you can see I render BBOX with the same CUBE MAP texture as I use for rendering the model so it looks like cool reflection/transparency effect :) (which was not intentional).
Anyway When I change the line
pixel_txr=(tm_l2g*vec4(pos,0.0)).xyz;
into:
pixel_txr=pos;
In my vertex shader the object will be solid again:
You can combine both by passing two texture coordinate vectors and fetching two texels in fragment adding them with some ratio together. Of coarse you would need to pass 2 Cube map textures one for object and one for skybox ...
The red warnings are from my CPU side code reminding me that I am trying to set uniforms that are not present in the shaders (as I did this from the bump mapping example without changing CPU side code...)
[Edit1] here preview of your mesh with offset
The Vertex changes a bit (just added the offsetting described in the answer):
//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_g2s;
uniform vec3 center=vec3(0.0,0.0,2.0);
layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;
out smooth vec4 pixel_col;
out smooth vec3 pixel_txr;
//------------------------------------------------------------------
void main(void)
{
pixel_col=col;
pixel_txr=pos-center;
gl_Position=tm_g2s*tm_l2g*vec4(pos,1.0);
}
//------------------------------------------------------------------
So by offsetting the center point you can get rid of the singular point distortion however as I mentioned in comments for arbitrary meshes there will be always some distortions with cheap texturing tricks instead of proper texture coordinates.
Beware my mesh was resized/normalized (sadly I do not remeber if its <-1,+1> range or different ona and too lazy to dig in my source code of the GLSL engine I tested this in) so the offset might have different magnitude in your environment to achieve the same result.

Diffuse lighting artifacts (OpenGL 4)

I'm trying to implement simplest possible diffuse lighting after reading a few tutorials, but fail miserably.
I load 3d mesh from Wavefront obj file and if I don't apply lighting, it looks just fine. But, when I do apply lighting, the animal looks like a chessboard and the cube is messed up even more:
animal comparison (with normals)
cube comparison (with normals)
Here is the vertex shader:
#version 400
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec3 a_texCoords;
layout (location = 2) in vec3 a_normal;
uniform mat4 u_viewProjection;
uniform mat4 u_model;
out vec3 v_fragPos;
out vec3 v_fragNormal;
void main() {
v_fragPos = a_position.xyz;
v_fragNormal = a_normal;
gl_Position = u_viewProjection * a_position;
}
I pass fragment position and normals to the fragment shader as-is because I'm not applying any model transformations, I simply assume that a model already has world coordinates after loading it from file (forget about u_model uniform, it's not used for now).
Then, the fragment shader:
#version 400
uniform vec3 u_lightPos;
uniform vec3 u_lightColor;
uniform vec3 u_diffuseColor;
uniform vec3 u_specularColor;
uniform vec3 u_ambientColor;
in vec3 v_fragPos;
in vec3 v_fragNormal;
out vec4 o_fragColor;
void main() {
vec3 lightDir = u_lightPos - v_fragPos;
float cosTheta = max(dot(normalize(v_fragNormal), normalize(lightDir)), 0.0);
vec3 diffuseContribution = cosTheta * u_lightColor;
o_fragColor = vec4(u_diffuseColor * diffuseContribution, 1.0);
}
No model or normal matrices used, because no rotations (or scale) are applied to model for now.
I thought about incorrect normals, but at least a simple cube should have correct ones, right?
Also, maybe I should mention that I'm using NSOpenGLView under MacOS.
Any help will be appreciated.
Thank you!
UPDATE:
Adding VBO setup.
This is how single vertex looks like:
struct Vertex1P1N1UV {
glm::vec4 mPosition;
glm::vec3 mTextureCoords;
glm::vec3 mNormal;
Vertex1P1N1UV();
Vertex1P1N1UV(glm::vec4 position, glm::vec3 texcoords, glm::vec3 normal);
};
I initialize my VAO like this
auto* VAO = new GLVertexArray<Vertex1P1N1UV>();
VAO->initialize(subMesh.vertices(), GLVertexArrayLayoutDescription({
static_cast<int>(glm::vec4::length() * sizeof(GLfloat)),
static_cast<int>(glm::vec3::length() * sizeof(GLfloat)),
static_cast<int>(glm::vec3::length() * sizeof(GLfloat)) }));
VAO initialize method
void initialize(const std::vector<Vertex>& vertices, const GLVertexArrayLayoutDescription& layoutDescription) {
bind();
mVertexBuffer.initialize(vertices);
GLuint offset = 0;
for (GLuint location = 0; location < layoutDescription.getAttributeSizes().size(); location++) {
glEnableVertexAttribArray(location);
GLsizei attributeSize = layoutDescription.getAttributeSizes()[location];
glVertexAttribPointer(location, attributeSize / sizeof(GLfloat), GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void *>(offset));
offset += attributeSize;
}
}
And buffer initialize method
void initialize(const std::vector<Vertex>& data) override {
bind();
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(Vertex), data.data(), GL_STATIC_DRAW);
}
Vertex will become Vertex1P1N1UV in this case
UPDATE:
Implemented normal visualization (reuploaded screenshots, scroll to top).
What bothers me is that I can see normals through the mesh, like it's transparent despite opaque color. Is this a depth testing problem?
After 2 days of struggling I found the problem.
I did not enable depth buffer on NSOpenGLView. That is one line of code.
If someone stumbles upon this, he can look here OpenGL GL_DEPTH_TEST not working
for a solution.

WebGL - which API to use?

I want to draw multiple polygon shapes (where each shape has it's own set of vertices).
I want to be able to position these shapes independently of each other.
Which API can i use to set the a_Position for the vertex shader?
A) gl.vertexAttrib3f
B) gl.vertexAttribPointer + gl.enableVertexAttribArray
thanks.
Your question makes it sound like you're really new to WebGL? Maybe you should read some tutorials? But in answer to your question:
gl.vertexAttrib3f only lets you supply a single constant value to a GLSL attribute so you'll need to use gl.vertexAttribPointer and gl.enableVertexAttribArray. You'll also need to set up buffers with your vertex data.
gl.vertexAttrib3f only point is arguably to let you pass in a constant in the case that you have a shader that uses multiple attributes but you don't have data for all of them. For example lets say you have a shader that uses both textures and so needs texture coordinates and it also has vertex colors. Something like this
vertex shader
attribute vec4 a_position;
attribute vec2 a_texcoord;
attribute vec4 a_color;
varying vec2 v_texcoord;
varying vec4 v_color;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * a_position;
// pass texcoord and vertex colors to fragment shader
v_texcoord = a_texcoord;
v_color = v_color;
}
fragment shader
precision mediump float;
varying vec2 v_texcoord;
varying vec4 v_color;
uniform sampler2D u_texture;
void main() {
vec4 textureColor = texture2D(u_texture, v_texcoord);
// multiply the texture color by the vertex color
gl_FragColor = textureColor * v_color;
}
This shader requires vertex colors. If your geometry doesn't have vertex colors then you have 2 options (1) use a different shader (2) turn off the attribute for vertex colors and set it to a constant color, probably white.
gl.disableVertexAttribArray(aColorLocation);
gl.vertexAttrib4f(aColorLocation, 1, 1, 1, 1);
Now you can use the same shader even though you have no vertex color data.
Similarly if you have no texture coordinates you could pass in a white 1 pixel shader and set the texture coordinates to some constant.
gl.displayVertexAttribArray(aTexcoordLocation);
gl.vertexAttrib2f(aTexcoordLocation, 0, 0);
gl.bindTexture(gl.TEXTURE_2D, some1x1PixelWhiteTexture);
In that case you could also decide what color to draw with by setting the vertex color attribute.
gl.vertexAttrib4f(aColorLocation, 1, 0, 1, 1); // draw in magenta

Resources