WEBGL applying animation to only specific objects among others - javascript

The objective of this work is to produce a big white circle in the center of the Canvas. Then on the circumference of the big circle, there will be 10 small circles of different colors. These 10 small circles will keep increasing in size.
My current work will cause the big white circle to increase in size too. How should I fix the size of the big white circle?
The only difference between draw_circle() and draw_growing_circle is that the later one has gl.uniform1f(u_SizeChange, currentSize); in the function.
I think it might be caused by how I initialize the VSHADER but I have no idea how to change it.
ps. In my codes, I only tried 2 small circles.
Thanks
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform float u_SizeChange;\n' +
'void main() {\n' +
' gl_Position.x = u_SizeChange * a_Position.x;\n' +
' gl_Position.y = u_SizeChange * a_Position.y;\n' +
' gl_Position.z = u_SizeChange * a_Position.z;\n' +
' gl_Position.w = 1.0;\n' +
'}\n';
var FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform vec4 u_FragColor;\n' +
'void main() {\n' +
' gl_FragColor = u_FragColor;\n' +
'}\n';
// Growing rate (size change/second)
var GROWING_RATE = 0.1;
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Specify the color for clearing <canvas>
console.log('run');
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Get storage location of u_SizeChange
var u_SizeChange = gl.getUniformLocation(gl.program, 'u_SizeChange');
if (!u_SizeChange) {
console.log('Failed to get the storage location of u_SizeChange');
return;
}
// Current size
var currentSize = 1.0;
var cx = 0;
var cy = 0;
var r = 200;
// Start drawing
var tick = function() {
currentSize = animate(currentSize); // Update the size
// draw(gl, n,currentSize, u_SizeChange); // Draw the square
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
draw_circle(gl, 0, 0, r/400,[1.0,1.0,1.0,1.0]); // Draw the Disk
draw_growing_circle(gl, cx+r*Math.sin(10)/400, cy+r*Math.cos(10)/400, 20/400,[Math.random(),Math.random(),Math.random(),1],currentSize, u_SizeChange); //Draw the bacteria
draw_growing_circle(gl, cx+r*Math.sin(20)/400, cy+r*Math.cos(20)/400, 20/400,[Math.random(),Math.random(),Math.random(),1],currentSize, u_SizeChange);
requestAnimationFrame(tick, canvas); // Request that the browser calls tick
};
tick();
}
var g_last = Date.now();
function animate(size) {
// Calculate the elapsed time
var now = Date.now();
var elapsed = now - g_last;
g_last = now;
// Update the current size (adjusted by the elapsed time)
var newSize = size + (GROWING_RATE * elapsed) / 100.0;
return newSize;
}
function draw_growing_circle(gl, x, y, r, color,currentSize,u_SizeChange){
//Define the geometry and store it in buffer objects
//Represent a circle disk through 360 trangles
n = 3*360; // number of total vertices
var vertices = [];
for (var i = 1; i <= 360; i++) {
vertices.push(x);
vertices.push(y);
vertices.push(x+r*Math.sin(i-1));
vertices.push(y+r*Math.cos(i-1));
vertices.push(x+r*Math.sin(i));
vertices.push(y+r*Math.cos(i));
}
// Create a new buffer object
var vertex_buffer = gl.createBuffer();
// Bind an empty array buffer to it
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
// Pass the vertices data to the buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
//Get the attribute location
var coord = gl.getAttribLocation(gl.program, "a_Position");
if(coord < 0) {
console.log('Failed to get the storage location of coord');
return -1;
}
// Get the storage location of u_FragColor
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
if (!u_FragColor) {
console.log('Failed to get the storage location of u_FragColor');
return;
}
//point an attribute to the currently bound VBO
gl.vertexAttribPointer(coord, 2, gl.FLOAT, false, 0, 0);
//Enable the attribute
gl.enableVertexAttribArray(coord);
//Drawing the required object (triangle)
// Enable the depth test
gl.enable(gl.DEPTH_TEST);
// Draw the triangles
// Pass the color of a point to u_FragColor variable
var rgba = color;
gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
gl.uniform1f(u_SizeChange, currentSize);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
// Unbind the buffer
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}

WebGL is a state machine, when you set a uniform it remains set until you set it to a different value, so when you draw your non-growing circle you want to make sure it has the corresponding value. Generating the geometry every frame is the wrong way of doing things as you only need to generate it once, but I'm getting a homework vibe from your questions so hopefully this topic will be addressed.

Related

Addressing Texel in ThreeJS DataTexture

I'm looking to compute texel references for a THREE.DataTexture in Javascript for use in a fragment shader. I've succeeded in computing screen space coordinates of points and passing them to a shader in a uniform float array of x and y values, and then referencing those points by indices in my shader. I now want to render too many points to pass the coordinates in a uniform float array so I'd like to use a DataTexture and write the coordinates in the RG values of RGBA texels.
Referencing this question I am using the following method:
var tDataWidth = points.length;
var tData = new Uint8Array( Math.pow(tDataWidth, 2) );
var texelSize = 1.0 / tDataWidth;
var texelOffset = new THREE.Vector2(0.5 * texelSize, 0.5 * texelSize);
for(var i = 0; i < points.length; i++){
//convert data to 0-1, then to 0-255
//inverse is to divide by 255 then multiply by width, height respectively
tData[i * 4] = Math.round(255 * (points[i].x / window.innerWidth));
tData[i * 4 + 1] = Math.round(255 * ((window.innerHeight - points[i].y) / window.innerHeight));
tData[i * 4 + 2] = 0;
tData[i * 4 + 3] = 0;
//calculate UV texel coordinates here
//Correct after edit
var u = ((i % tDataWidth) / tDataWidth) + texelOffset;
var v = (Math.floor(i / tDataWidth) + texelOffset);
var vUV = new THREE.Vector2(u, v);
//this function inserts the reference to the texel at the index into the shader
//referenced in the frag shader:
//cvec = texture2D(tData, index);
shaderInsert += ShaderInsert(vUV, screenPos.x, window.innerHeight - screenPos.y);
}
var dTexture = new THREE.DataTexture( sdfUItData, tDataWidth, tDataWidth, THREE.RGBAFormat, THREE.UnsignedByteType );
//I think this is necessary
dTexture.magFilter = THREE.NearestFilter;
dTexture.needsUpdate = true;
//update uniforms of shader to get this DataTexture
renderer.getUniforms("circles")["tData"].value = dTexture;
//return string insert of circle
//I'm editing the shader through javascript then recompiling it
//There's more to it in the calling function, but this is the relevant part I think
...
ShaderInsert(index){
var circle = "\n\tvIndex = vec2(" + String(index.x) + ", " + String(index.y) + ");\n";
circle += "\tcvec = texture2D(tData, vIndex);\n";
circle += "\tcpos = vec2( (cvec.r / 255.0) * resolution.x, (cvec.y / 255.0) * resolution.y);\n";
circle += "\tc = circleDist(translate(p, cpos), 7.0);\n";
circle += "\tm = merge(m, c);";
return(circle);
}
Any help on where I'm going wrong? Right now output is all in the lower left corner, so (0, window.innerHeight) as far as I can tell. Thanks!
So the answer is actually straightforward. In the fragment shader rgba values are 0.0 - 1.0, so there's no need to divide by 255 as I was doing in the fragment shader.
I'd also like to say that I discovered the Spector.js Chrome extension which allows one to view all webgl calls and buffers. Pretty cool!
If anyone wants to learn more about how the drawing functions work in the fragment shader, it's all in this awesome shader which I did not write:
https://www.shadertoy.com/view/4dfXDn
<3

Post Effects and Transparent background in three.js

Trying to use the transparent background with some post effect like the Unreal Bloom, SMAA and Tonemapping provided in the examples but it seems to break the transparency from my render.
renderer = new THREE.WebGLRenderer({ canvas, alpha: true });
renderer.setClearColor(0xFF0000, 0);
composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
// Bloom pass
canvasSize = new THREE.Vector2(canvas.width, canvas.height);
pass = new UnrealBloomPass(canvasSize, strength, radius, threshhold);
composer.addPass(pass);
// SMAA pass
size = canvasSize.multiplyScalar(this.renderer.getPixelRatio());
pass = new SMAAPass(size.x, size.y);
pass.renderToScreen = true
composer.addPass(pass);
// Tonemapping
renderer.toneMappingExposure = exposure;
renderer.toneMappingWhitePoint = whitePoint;
renderer.toneMapping = type;
composer.render();
If I deactivate the bloom pass I get a correct transparent background but when activated, I obtain a black background. I looked at the sources and it seems that it should correctly handle alpha texture channel as the format is set correctly to THREE.RGBAFormat.
Edit: After some research, I found where does this comes from. It comes from getSeperableBlurMaterial in js\postprocessing\UnrealBloomPass.js.
The fragment's alpha channel is always set to 1.0 which results in a complete removal of the previous alpha values when doing the additive blending at the end.
The cool thing would be to find a proper way to apply the alpha inside the Gaussian blur. Any idea how ?
I found a solution and this can be sorted like this :
https://github.com/mrdoob/three.js/issues/14104
void main()
{
vec2 invSize = 1.0 / texSize;
float fSigma = float(SIGMA);
float weightSum = gaussianPdf(0.0, fSigma);
float alphaSum = 0.0;
vec3 diffuseSum = texture2D(colorTexture, vUv).rgb * weightSum;
for( int i = 1; i < KERNEL_RADIUS; i ++ )
{
float x = float(i);
float weight = gaussianPdf(x, fSigma);
vec2 uvOffset = direction * invSize * x;
vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);
float weightAlpha = sample1.a * weight;
diffuseSum += sample1.rgb * weightAlpha;
alphaSum += weightAlpha;
weightSum += weight;
vec4 sample2 = texture2D( colorTexture, vUv - uvOffset);
weightAlpha = sample2.a * weight;
diffuseSum += sample2.rgb * weightAlpha;
alphaSum += weightAlpha;
weightSum += weight;
}
alphaSum /= weightSum;
diffuseSum /= alphaSum; // Should apply discard here if alphaSum is 0
gl_FragColor = vec4(diffuseSum.rgb, alphaSum);
}

Unable to load texture in WebGl

I am able to load RGB colours but not textures. If it could be some settings problem please prompt me.
This is the screenshot of chrome://flags
The HTML code is given :
<!DOCTYPE html>
<meta charset="UTF-8">
<html>
<head>
<title>WebGL Cube with Texture</title>
<script type="x-shader/x-vertex" id="vshader">
attribute vec3 coords;
attribute vec2 texCoords;
uniform vec3 normal;
uniform mat4 modelview;
uniform mat4 projection;
uniform mat3 normalMatrix;
varying vec3 vNormal;
varying vec2 vTexCoords;
void main() {
vec4 coords = vec4(coords,1.0);
vec4 transformedVertex = modelview * coords;
vNormal = normalMatrix * normal;
vTexCoords = texCoords;
gl_Position = projection * transformedVertex;
}
</script>
<script type="x-shader/x-fragment" id="fshader">
precision mediump float;
uniform bool textured;
uniform sampler2D sampler;
varying vec3 vNormal;
varying vec2 vTexCoords;
uniform vec4 color;
void main() {
if (textured) {
vec4 color = texture2D(sampler, vTexCoords);
vec3 unitNormal = normalize(vNormal);
float multiplier = abs(unitNormal.z);
gl_FragColor = vec4( multiplier*color.r, multiplier*color.g, multiplier*color.b, color.a );
}
else {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); // use basic white when texture's not there.
}
}
</script>
<script type="text/javascript" src="gl-matrix-min.js"></script>
<script type="text/javascript" src="simple-rotator.js"></script>
<script type="text/javascript">
"use strict";
var gl; // The webgl context.
var aCoords; // Location of the coords attribute variable in the shader program.
var aCoordsBuffer; // Buffer to hold coords.
var aTexCoords; // Location of the texCoords attribute variable in the shader program.
var aTexCoordsBuffer; // Buffer to hold texCoords.
var uProjection; // Location of the projection uniform matrix in the shader program.
var uModelview; // Location of the modelview unifirm matrix in the shader program.
var uNormal; // Location of the normal uniform in the shader program.
var uColor; // Location of the color uniform in the shader program, used only for axes.
var uTextured; // Location of the textured uniform in the shader program.
var uSampler; // Location of the sampler in the shader program.
var uNormalMatrix; // Location of the normalMatrix uniform matrix in the shader program.
var projection = mat4.create(); // projection matrix
var modelview = mat4.create(); // modelview matrix
var normalMatrix = mat3.create(); // matrix, derived from modelview matrix, for transforming normal vectors
var rotator; // A SimpleRotator object to enable rotation by mouse dragging.
var textureID = null; // Texture object, to be created after image has loaded.
/* Draws a colored cube, along with a set of coordinate axes.
* (Note that the use of the above drawPrimitive function is not an efficient
* way to draw with WebGL. Here, the geometry is so simple that it doesn't matter.)
*/
function draw() {
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if (document.getElementById("persproj").checked) {
mat4.perspective(projection, Math.PI/4, 1, 2, 10);
}
else {
mat4.ortho(projection,-2.5, 2.5, -2.5, 2.5, 2, 10);
}
gl.uniformMatrix4fv(uProjection, false, projection );
var modelview = rotator.getViewMatrix();
var saveModelview = mat4.clone(modelview);
if (textureID) {
gl.uniform1i( uTextured, 1 ); // Tell shader to use texture and lighting.
gl.bindTexture(gl.TEXTURE_2D, textureID); // Which texture should be used.
gl.uniform1i(uSampler, 0); // Set sampler in shadre to use texture unit zero.
}
else {
gl.uniform1i( uTextured, 0 ); // Cube will appear in plain white.
}
drawFace(modelview) // front face of the cube
mat4.rotateY(modelview,modelview,Math.PI/2); //right face
drawFace(modelview) // front face
mat4.rotateY(modelview,modelview,Math.PI/2); //back face
drawFace(modelview) // front face
mat4.rotateY(modelview,modelview,Math.PI/2); //left face
drawFace(modelview) // front face
modelview = mat4.clone(saveModelview);
mat4.rotateX(modelview,modelview,Math.PI/2);
drawFace(modelview) // top face
mat4.rotateX(modelview,modelview,Math.PI);
drawFace(modelview) // bottom face
}
/**
* Draws the front face of the cube, subject to a modelview transform.
*/
function drawFace(modelview) {
gl.uniformMatrix4fv(uModelview, false, modelview );
mat3.normalFromMat4(normalMatrix, modelview);
gl.uniformMatrix3fv(uNormalMatrix, false, normalMatrix);
gl.uniform3f(uNormal, 0, 0, 1);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // front face
}
/**
* Loads data for the front face of the cube into VBOs.
*/
function createFace() {
var vertices = [ -1,-1,1, 1,-1,1, 1,1,1, -1,1,1 ];
var texCoords = [ 0,0, 2,0, 2,2, 0,2 ];
gl.enableVertexAttribArray(aCoords);
gl.bindBuffer(gl.ARRAY_BUFFER,aCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(aCoords, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aTexCoords);
gl.bindBuffer(gl.ARRAY_BUFFER,aTexCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);
gl.vertexAttribPointer(aTexCoords, 2, gl.FLOAT, false, 0, 0);
}
/**
* Load an image from the URL "textures/bridk001.jpg". The image is loade
* asynchronously. When the
*/
function loadTexture() {
var img = new Image();
img.onload = function() {
var id = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D,id);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
textureID = id;
draw();
}
img.src = "./skin.jpg";
}
/* Creates a program for use in the WebGL context gl, and returns the
* identifier for that program. If an error occurs while compiling or
* linking the program, an exception of type String is thrown. The error
* string contains the compilation or linking error. If no error occurs,
* the program identifier is the return value of the function.
*/
function createProgram(gl, vertexShaderSource, fragmentShaderSource) {
var vsh = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource(vsh,vertexShaderSource);
gl.compileShader(vsh);
if ( ! gl.getShaderParameter(vsh, gl.COMPILE_STATUS) ) {
throw "Error in vertex shader: " + gl.getShaderInfoLog(vsh);
}
var fsh = gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource(fsh, fragmentShaderSource);
gl.compileShader(fsh);
if ( ! gl.getShaderParameter(fsh, gl.COMPILE_STATUS) ) {
throw "Error in fragment shader: " + gl.getShaderInfoLog(fsh);
}
var prog = gl.createProgram();
gl.attachShader(prog,vsh);
gl.attachShader(prog, fsh);
gl.linkProgram(prog);
if ( ! gl.getProgramParameter( prog, gl.LINK_STATUS) ) {
throw "Link error in program: " + gl.getProgramInfoLog(prog);
}
return prog;
}
/* Gets the text content of an HTML element. This is used
* to get the shader source from the script elements that contain
* it. The parameter should be the id of the script element.
*/
function getTextContent( elementID ) {
var element = document.getElementById(elementID);
var fsource = "";
var node = element.firstChild;
var str = "";
while (node) {
if (node.nodeType == 3) // this is a text node
str += node.textContent;
node = node.nextSibling;
}
return str;
}
/**
* Initializes the WebGL program including the relevant global variables
* and the WebGL state. Creates a SimpleView3D object for viewing the
* cube and installs a mouse handler that lets the user rotate the cube.
*/
function init() {
try {
var canvas = document.getElementById("glcanvas");
gl = canvas.getContext("webgl");
if ( ! gl ) {
gl = canvas.getContext("experimental-webgl");
}
if ( ! gl ) {
throw "Could not create WebGL context.";
}
var vertexShaderSource = getTextContent("vshader");
var fragmentShaderSource = getTextContent("fshader");
var prog = createProgram(gl,vertexShaderSource,fragmentShaderSource);
gl.useProgram(prog);
aCoords = gl.getAttribLocation(prog, "coords");
aTexCoords = gl.getAttribLocation(prog, "texCoords");
uModelview = gl.getUniformLocation(prog, "modelview");
uProjection = gl.getUniformLocation(prog, "projection");
uSampler = gl.getUniformLocation(prog, "sampler");
uNormal = gl.getUniformLocation(prog, "normal");
uColor = gl.getUniformLocation(prog, "color");
uTextured = gl.getUniformLocation(prog, "textured");
uNormalMatrix = gl.getUniformLocation(prog, "normalMatrix");
aCoordsBuffer = gl.createBuffer();
aTexCoordsBuffer = gl.createBuffer();
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE); // no need to draw back faces
document.getElementById("persproj").checked = true;
rotator = new SimpleRotator(canvas,draw);
rotator.setView( [2,2,5], [0,1,0], 6 );
}
catch (e) {
document.getElementById("message").innerHTML =
"Could not initialize WebGL: " + e;
return;
}
createFace();
loadTexture();
draw();
}
</script>
</head>
<body onload="init()" style="background-color:#DDD">
<h2>A Cube With a Brick Texture</h2>
<p id=message>Drag the mouse on the canvas to rotate the view.</p>
<p>
<input type="radio" name="projectionType" id="persproj" value="perspective" onchange="draw()">
<label for="persproj">Perspective projection</label>
<input type="radio" name="projectionType" id="orthproj" value="orthogonal" onchange="draw()" style="margin-left:1cm">
<label for="orthproj">Orthogonal projection</label>
<button onclick="rotator.setView( [2,2,5], [0,1,0], 6 ); draw()" style="margin-left:1cm">Reset View</button>
</p>
<noscript><hr><h3>This page requires Javascript and a web browser that supports WebGL</h3><hr></noscript>
<div>
<canvas width=600 height=600 id="glcanvas" style="background-color:red"></canvas>
</div>
</body>
</html>
All i get as an output is
The other functions are loading fine. The file paths are correct.
The issue is you need to run a simple web server for WebGL dev. It should take you about 2 minutes to get setup
See this
Try defining the minification and magnification parameters for the texture object.
eg:
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
Use the appropriate value for min and mag filter, based on your project requirement.

initiate a number of vertices/triangles for vertex shader to use

I've been playing around with vertexshaderart.com and I'd like to use what I learned on a separate website. While I have used shaders before, some effects achieved on the site depend on having access to vertices/lines/triangles. While passing vertices is easy enough (at least it was with THREE.js, though it is kind of an overkill for simple shaders, but in some cases in need shader materials too), creating triangles seems a bit more complex.
I can't figure it out from the source, how exactly are triangles created there, when you switch the mode here?
I'd like to replicate that behavior but I honestly have no idea how to approach it. I could just create a number of triangles through THREE but with so many individual objects performance takes a hit rapidly. Are the triangles created here separate entities or are they a part of one geometry?
vertexshaderart.com is more of a puzzle, toy, art box, creative coding experiment than an example of the good WebGL. The same is true of shadertoy.com. An example like this is beautiful but it runs at 20fps in it's tiny window and about 1fps fullscreen on my 2014 Macbook Pro and yet my MBP can play beautiful games with huge worlds rendered fullscreen at 60fps. In other words, the techniques are more for art/fun/play/mental exercise and for the fun of trying to make things happen with extreme limits than to actually be good techniques.
The point I'm trying to make is both vertexshaderart and shadertoy are fun but impractical.
The way vertexshaderart works is it provides a count vertexId that counts vertices. 0 to N where N is the count setting the top of the UI. For each count you output gl_Position and a v_color (color).
So, if you want to draw something you need to provide the math to generate vertex positions based on the count. For example let's do it using Canvas 2D first
Here's a fake JavaScript vertex shader written in JavaScript that given nothing but vertexId will draw a grid 1 unit high and N units long where N = the number of vertices (vertexCount) / 6.
function ourPseudoVertexShader(vertexId, time) {
// let's compute an infinite grid of points based off vertexId
var x = Math.floor(vertexId / 6) + (vertexId % 2);
var y = (Math.floor(vertexId / 2) + Math.floor(vertexId / 3)) % 2;
// color every other triangle red or green
var triangleId = Math.floor(vertexId / 3);
var color = triangleId % 2 ? "#F00" : "#0F0";
return {
x: x * 0.2,
y: y * 0.2,
color: color,
};
}
We call it from a loop supplying vertexId
for (var count = 0; count < vertexCount; count += 3) {
// get 3 points
var position0 = ourPseudoVertexShader(count + 0, time);
var position1 = ourPseudoVertexShader(count + 1, time);
var position2 = ourPseudoVertexShader(count + 2, time);
// draw triangle
ctx.beginPath();
ctx.moveTo(position0.x, position0.y);
ctx.lineTo(position1.x, position1.y);
ctx.lineTo(position2.x, position2.y);
ctx.fillStyle = position0.color;
ctx.fill();
}
If you run it here you'll see a grid 1 unit high and N units long. I've set the canvas origin so 0,0 is in the center just like WebGL and so the canvas is addressed +1 to -1 across and +1 to -1 down
var vertexCount = 100;
function ourPseudoVertexShader(vertexId, time) {
// let's compute an infinite grid of points based off vertexId
var x = Math.floor(vertexId / 6) + (vertexId % 2);
var y = (Math.floor(vertexId / 2) + Math.floor(vertexId / 3)) % 2;
// color every other triangle red or green
var triangleId = Math.floor(vertexId / 3);
var color = triangleId % 2 ? "#F00" : "#0F0";
return {
x: x * 0.2,
y: y * 0.2,
color: color,
};
}
var ctx = document.querySelector("canvas").getContext("2d");
requestAnimationFrame(render);
function render(time) {
time *= 0.001;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.scale(ctx.canvas.width / 2, -ctx.canvas.height / 2);
// lets assume triangles
for (var count = 0; count < vertexCount; count += 3) {
// get 3 points
var position0 = ourPseudoVertexShader(count + 0, time);
var position1 = ourPseudoVertexShader(count + 1, time);
var position2 = ourPseudoVertexShader(count + 2, time);
// draw triangle
ctx.beginPath();
ctx.moveTo(position0.x, position0.y);
ctx.lineTo(position1.x, position1.y);
ctx.lineTo(position2.x, position2.y);
ctx.fillStyle = position0.color;
ctx.fill();
}
ctx.restore();
requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas width="500" height="200"></canvas>
Doing the same thing in WebGL means making a buffer with the count
var count = [];
for (var i = 0; i < vertexCount; ++i) {
count.push(i);
}
Then putting that count in a buffer and using that as an attribute for a shader.
Here's the equivalent shader to the fake shader above
attribute float vertexId;
uniform float time;
varying vec4 v_color;
void main() {
// let's compute an infinite grid of points based off vertexId
float x = floor(vertexId / 6.) + mod(vertexId, 2.);
float y = mod(floor(vertexId / 2.) + floor(vertexId / 3.), 2.);
// color every other triangle red or green
float triangleId = floor(vertexId / 3.);
v_color = mix(vec4(0, 1, 0, 1), vec4(1, 0, 0, 1), mod(triangleId, 2.));
gl_Position = vec4(x * 0.2, y * 0.2, 0, 1);
}
If we run that we'll get the same result
var vs = `
attribute float vertexId;
uniform float vertexCount;
uniform float time;
varying vec4 v_color;
void main() {
// let's compute an infinite grid of points based off vertexId
float x = floor(vertexId / 6.) + mod(vertexId, 2.);
float y = mod(floor(vertexId / 2.) + floor(vertexId / 3.), 2.);
// color every other triangle red or green
float triangleId = floor(vertexId / 3.);
v_color = mix(vec4(0, 1, 0, 1), vec4(1, 0, 0, 1), mod(triangleId, 2.));
gl_Position = vec4(x * 0.2, y * 0.2, 0, 1);
}
`;
var fs = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
var vertexCount = 100;
var gl = document.querySelector("canvas").getContext("webgl");
var count = [];
for (var i = 0; i < vertexCount; ++i) {
count.push(i);
}
var bufferInfo = twgl.createBufferInfoFromArrays(gl, {
vertexId: { numComponents: 1, data: count, },
});
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
var uniforms = {
time: 0,
vertexCount: vertexCount,
};
requestAnimationFrame(render);
function render(time) {
uniforms.time = time * 0.001;
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<canvas width="500" height="200"></canvas>
Everything else on vertexshartart is just creative math to make interesting patterns. You can use time to do animation. a texture with sound data is also provided.
There are some tutorials here
So, in answer to your question, when you switch modes (triangles/lines/points) on vertexshaderart.com all that does is change what's passed to gl.drawArrays (gl.POINTS, gl.LINES, gl.TRIANGLES). The points themselves are generated in the vertex shader like the example above.
So that leaves the question, what specific effect are you trying to achieve. Then we can know what to suggest to achieve it. You might want to ask a new question for that (so that this answer still matches the question above)

WebGL Fragment Shader constructor error - Too many arguments

I'm working on a Javascript/OpenGL(WebGL) program that's supposed to make a 3D cube with a texture colored to look like a Rubik's cube. So far, I have the texture mapped just fine, and every point in the cube seems to be showing properly.
However, I'm having a tough time getting the color to display on it without getting some errors from the fragment shader. At the moment, I get this one error I've been trying to figure out with no luck. Here's the error message.
Error: fragment shader compiler: ERROR: 0:11: 'constructor' : too many arguments
Here's my fragment shader code to look at specifically.
<script id="fragment-shader" type="x-shader">
precision mediump float;
uniform sampler2D image0;
varying vec2 textureCoordinatesV;
varying vec4 pointColorV;
void main()
{
// extract the RGBA color from image at given coordinates.
vec4 oldColor = texture2D( image0, textureCoordinatesV );
vec4 newColor;
newColor = vec4(oldColor.r * pointColorV, oldColor.g * pointColorV, oldColor.b * pointColorV, 1.0);
gl_FragColor = newColor;
}
</script>
Here's the image I'm using for this program.
http://tinypic.com/view.php?pic=2rpbbqq&s=9#.Vkj3jPmrTIU
And here's my code in it's entirety. I am aware of three.js to render 3D objects in WebGL, but this current approach is for me to get a better understanding of how to render the object with existing tools. Also, if you're having trouble seeing the cube, try running this program in Firefox.
There is a Vector4.js and Matrix4.js I'm using with this program, but from what I understand, they don't have anything to do with the current error message I'm receiving. Vector4 is just a script that establishes a Vector4 structure for me to use to hold vertices to render, and Matrix4 is a script for moving those vertices once they're established. If need be, I will post these files in the comments.
Any and all help would be appreciated on figuring out why this error keeps coming up, thank you very much.
<html>
<head>
<title>Template WebGL file</title>
</head>
<body onload="main()">
<canvas id="myCanvas" width="400" height="400"></canvas>
<!-- Load external file with helper setup functions. -->
<script src="webgl-utils.js"></script>
<script src="Vector4.js"></script>
<script src="Matrix4.js"></script>
<!-- vertex shader code -->
<script id="vertex-shader" type="x-shader">
attribute vec4 pointPosition;
uniform mat4 transformation;
attribute vec4 pointColorA;
varying vec4 pointColorV;
attribute vec2 textureCoordinatesA;
varying vec2 textureCoordinatesV;
void main()
{
gl_Position = transformation * pointPosition;
textureCoordinatesV = textureCoordinatesA;
pointColorV = pointColorA;
}
</script>
<!-- fragment shader code -->
<script id="fragment-shader" type="x-shader">
precision mediump float;
uniform sampler2D image0;
varying vec2 textureCoordinatesV;
varying vec4 pointColorV;
void main()
{
// extract the RGBA color from image at given coordinates.
vec4 oldColor = texture2D( image0, textureCoordinatesV );
vec4 newColor;
newColor = vec4(oldColor.r * pointColorV, oldColor.g * pointColorV, oldColor.b * pointColorV, 1.0);
gl_FragColor = newColor;
}
</script>
<!-- main Javascript program -->
<script>
// DECLARE GLOBAL JAVASCRIPT VARIABLES
var canvas = document.getElementById('myCanvas');
var gl; // the WebGL context
var transformationData, transformationAddress;
var x, y, z, angle, scale, time;
function main()
{
// STANDARD STARTUP CODE
gl = setupWebGL(canvas);
var program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
gl.enable(gl.DEPTH_TEST);
// WRITE MAIN JAVASCRIPT CODE HERE
// [x,y,z, r,g,b]
var v0 = [0,0,0]; // back; black
var v1 = [0,0,1]; // z axis; blue
var v2 = [0,1,0]; // y axis; green
var v3 = [0,1,1]; // y+z; cyan
var v4 = [1,0,0]; // x axis; red
var v5 = [1,0,1]; // x+z; magenta
var v6 = [1,1,0]; // x+y; yellow
var v7 = [1,1,1]; // all; white
var uv0 = [0,0];
var uv1 = [0,1];
var uv2 = [1,0];
var uv3 = [1,1];
var cr = [1,0,0]; //red
var cg = [0,1,0]; //green
var cb = [0,0,1]; //blue
var cy = [1,1,0]; //yellow
var co = [1,0.5,0]; //orange
var cw = [1,1,1]; //white
var data = [];
// left x2, right x2, front x2, back x2, up x2, down x2
data = data.concat( v7,uv3,cr,v3,uv1,cr,v1,uv0,cr, v7,uv3,cr,v1,uv0,cr,v5,uv2,cr, // front
v3,uv3,cy,v2,uv1,cy,v0,uv0,cy, v3,uv3,cy,v0,uv0,cy,v1,uv2,cy, // left
v6,uv3,cb,v2,uv1,cb,v3,uv0,cb, v6,uv3,cb,v3,uv0,cb,v7,uv2,cb, // up
v2,uv3,co,v6,uv1,co,v4,uv0,co, v2,uv3,co,v4,uv0,co,v0,uv2,co, // back
v6,uv3,cw,v7,uv1,cw,v5,uv0,cw, v6,uv3,cw,v5,uv0,cw,v4,uv2,cw, // right
v5,uv3,cg,v1,uv1,cg,v0,uv0,cg, v5,uv3,cg,v0,uv0,cg,v4,uv2,cg, v0 ); // down
var attributeArray = new Float32Array( data );
var attributeBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, attributeBuffer );
gl.bufferData( gl.ARRAY_BUFFER, attributeArray, gl.STATIC_DRAW );
var pointPositionAddress =
gl.getAttribLocation( program, "pointPosition" );
var textureCoordinatesAddress =
gl.getAttribLocation( program, "textureCoordinatesA" );
var pointColorAddress =
gl.getAttribLocation( program, "pointColorA" );
var BPE = attributeArray.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(
pointPositionAddress, 3, gl.FLOAT, false, 8*BPE, 0*BPE );
gl.enableVertexAttribArray( pointPositionAddress );
gl.vertexAttribPointer(
textureCoordinatesAddress, 3, gl.FLOAT, false, 8*BPE, 3*BPE );
gl.enableVertexAttribArray( textureCoordinatesAddress );
gl.vertexAttribPointer(
pointColorAddress, 3, gl.FLOAT, false, 8 * BPE, 4*BPE);
gl.enableVertexAttribArray(pointColorAddress);
transformationAddress = gl.getUniformLocation( program, "transformation" );
transformationData = new Matrix4().setIdentity();
x = 0;
y = 0;
z = 0;
angle = 0;
scale = 1;
time = 0;
// set up texture buffer
var textureBuffer = gl.createTexture();
var imageAddress = gl.getUniformLocation( program, "image0" );
var imageData = new Image();
// when image is done loading run some code (load into GPU)
imageData.onload = function() {
loadTexture0(textureBuffer, imageData, imageAddress); }
// start loading the image
imageData.src = "DDBingoGrid.png";
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
loop();
}
// OTHER FUNCTIONS
function loadTexture0( tb, id, ia )
{
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture(gl.TEXTURE_2D, tb);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB,
gl.UNSIGNED_BYTE, id);
gl.uniform1i( ia, 0 );
}
function loop()
{
update();
render();
setTimeout( loop, 16 );
}
// update JavaScript variables; send new data to GPU
function update()
{
time += 0.016; // 60 FPS!
x += 0.00;
y += 0.00;
z += 0.00;
angle += 0.01;
scale += 0.00;
var model = new Matrix4();
var t = new Matrix4();
t.setTranslation(-1/2, 0, -1/2);
var rx = new Matrix4();
rx.setRotationX( angle );
var ry = new Matrix4();
ry.setRotationY(angle);
model = rx.multiply(ry).multiply(t);
var camera = new Matrix4();
camera.setTranslation(0,0,6);
var view = camera.inverse();
var projection = new Matrix4();
projection.setPerspective( 45, 1, 0.1, 100 );
transformationData = projection.multiply(view).multiply(model);
gl.uniformMatrix4fv( transformationAddress, false, transformationData.toArray() );
}
// draw all the things
function render()
{
gl.clear( gl.COLOR_BUFFER_BIT );
gl.clear( gl.DEPTH_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLES, 0, 36 );
}
</script>
</body>
</html>
The problem lies in this line:
newColor = vec4(oldColor.r * pointColorV,
oldColor.g * pointColorV,
oldColor.b * pointColorV,
1.0);
In glsl, the float*vector operation returns a vector. Since pointColorV is a vector, you try to pass three vec4 objects into the vec4 constructor, which is not possible. You can solve that by adding the correct swizzle operator .r/.g/.b after pointColorV. But the better option is to write the whole thing as one operation:
newColor = vec4(oldColor.rgb * pointColorV.rgb, 1.0);

Categories

Resources