How to shape plane like a voronoi cell with vertex shader? - javascript

I'm new to the world of shaders and I am stuck on the following problem:
I'm trying to make a voronoi diagram with several planes in THREE.js. Each plane represents a cell of the diagram. Actually, I managed to create the right shader fragment for each plane. The colored space of the plane corresponds to the space taken by the cell in the voronoi diagram. Elsewhere the plane is transparent. Now I would like to modify the vertex so that the plane is exactly the shape of the cell because later I would like to be able to click on the different cells.
Fragment shader :
uniform float u_time;
uniform float u_speed;
uniform vec3 u_position; //plane position
uniform vec3 u_allPositions[10]; //All plane positions
varying vec2 vUv;
// Random 2D vector
vec2 random2( vec2 p ) {
return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
}
void main() {
vec4 color= vec4(0.0);
float m_dist = 1.0;
for (int i=0; i<10; i++) {
// Random position in the neighbor
vec2 point = vec2(u_allPositions[i].x, u_allPositions[i].y);
// Animate the point
point = 0.5 + 0.5 * sin(u_speed * u_time + 6.2831 * point );
// Vector between the pixel and the point
vec2 diff = point - fract(vUv);
// Distance to the point
float dist = length(diff);
// Keep the closer distance
m_dist = min(m_dist, dist);
}
// Select the right point
vec2 point = vec2(u_position.x, u_position.y);
point = 0.5 + 0.5 * sin(u_speed * u_time + 6.2831 * point );
vec2 diff = point - fract(vUv);
float dist = length(diff);
if (dist <= m_dist) {
color += vec4(vec3(dist), 1.0);
// Accentuate the point center
color.rgb += 1.0 - step(0.02, m_dist);
}
gl_FragColor = vec4(color);
}
My javascript (probably useless)
import React from "react";
import * as THREE from "three";
import { Canvas, useFrame } from "#react-three/fiber";
import vertexShader from "../shaders/plane.vert";
import fragmentShader from "../shaders/plane.frag";
const randomPoints = (xMin, xMax, yMin, yMax, n) => {
return Array.from({ length: n }, () => {
return {
x: Math.random() * (xMax - xMin) + xMin,
y: Math.random() * (yMax - yMin) + yMin,
z: 0,
};
});
};
const tenRandomPoints = randomPoints(-5, 5, -3, 3, 10);
const tenRandomPointsNormalized = tenRandomPoints.map((point) => {
return {
x: (point.x + 5) / 10,
y: (point.y + 3) / 6,
z: 0,
};
});
const planeUniforms = tenRandomPointsNormalized.map((point, index) => {
return {
u_time: { value: 0 },
u_speed: { value: 0.7 },
u_position: {
value: new THREE.Vector3(point.x, point.y, point.z),
},
u_allPositions: {
value: tenRandomPointsNormalized.map(
(point) => new THREE.Vector3(point.x, point.y, point.z)
),
type: "v3v[]",
},
};
});
const Varonoi = () => {
useFrame(({ clock }) => {
planeUniforms.forEach((uniform) => {
uniform.u_time.value = clock.getElapsedTime();
});
});
return (
<group>
{tenRandomPoints.map((point, index) => (
<mesh key={index} position={[point.x, point.y, point.z]} scale={1}>
<planeGeometry args={[1, 1, 10, 10]} />
<shaderMaterial
vertexShader={vertexShader}
fragmentShader={fragmentShader}
uniforms={planeUniforms[index]}
transparent={true}
/>
</mesh>
))}
</group>
);
};
export default Varonoi;
I tried to modify the 'modelPosition' variable of the vertex but without success.
I'm not sure I fully understand how the vertex positions are calculated.
Thanks for your help.

Related

WebGl Triangle Rotation Problems: Triangle change size and get skewed

Dear Firend : I am new at WebGl. I have managed to draw and rotate a triangle
-- but the problem is that the triangle
1, The triangle change size and shape while drawing.
2, I dont know how to rotate the triangle around one of its cornor.
Following is the code. I have written the code in a linear fashion with a utility class (GlUtil) that wraps up the boring tasks.
I am using a function called perc2glCoord that allow me to enter percentages and convert them into gl coordinates.
import GlUtil from "./glUtil/glUtil.js";
import perc2glCoord from "./functions/perc2glCoord.js";
const gl = GlUtil.getGl("bilza");
console.log(gl);
const vertices = [
perc2glCoord (50) ,perc2glCoord (50), 1,0,0,
perc2glCoord (50) ,perc2glCoord (75), 0,1,0,
perc2glCoord (75) ,perc2glCoord (50), 0,0,1,
// perc2glCoord (25) ,perc2glCoord (50), 1,0,0,
// perc2glCoord (75) ,perc2glCoord (50), 0,1,0,
// perc2glCoord (50) ,perc2glCoord (75), 0,0,1,
];
const vertexShaderSrc =
`
attribute highp vec2 a_pos;
attribute highp vec3 a_clr;
uniform float translateX;
uniform float translateY;
uniform float angle;
varying highp vec3 vColor;
void main(void) {
gl_Position = vec4(
translateX + (a_pos.x * cos(angle) - a_pos.y * sin(angle)),
translateY + (a_pos.x * sin(angle) + a_pos.y * cos(angle)),
1.0,
1.0 );
vColor = a_clr;
}
`;
const fragShaderSrc =
`
varying highp vec3 vColor;
void main(void) {
gl_FragColor = vec4 (vColor , 1.0);
}
`;
const vertexShader = GlUtil.createShader(gl,vertexShaderSrc,gl.VERTEX_SHADER);
const fragmentShader = GlUtil.createShader(gl,fragShaderSrc,gl.FRAGMENT_SHADER);
const programe = GlUtil.getProgram(gl,vertexShader,fragmentShader);
const VOB = GlUtil.getBuffer(gl);
GlUtil.bindBuffer(gl,VOB,vertices);
GlUtil.linkNuseProgram(gl,programe);
let angleValue = 0;
function draw(){
GlUtil.setAttribute(gl,"a_pos",programe, 2 ,4*5,0);
GlUtil.setAttribute(gl,"a_clr",programe, 3 , 4*5,2 * 4);
const translateXLoc = gl.getUniformLocation(programe, "translateX");
gl.uniform1f(translateXLoc,0.0);
const translateYLoc = gl.getUniformLocation(programe, "translateY");
gl.uniform1f(translateYLoc,0.0);
const angleLoc = gl.getUniformLocation(programe, "angle");
const rands = Math.PI * angleValue /180;
gl.uniform1f(angleLoc,rands);
angleValue+= 0.1;
/////////////////////---draw-----------------
GlUtil.clear(gl,0.1,0.1,0.2);
gl.drawArrays(gl.TRIANGLES , 0, 3);
requestAnimationFrame(draw);
}
draw();
---
Here is the GlUtil helper object
export default class GlUtil {
static getGl(canvasId :string ="bilza"):WebGLRenderingContext{
const canvas = document.getElementById(canvasId) as HTMLCanvasElement;
if (canvas == null){
throw new Error("canvas not found");
}
const gl = canvas.getContext("webgl");
if (gl == null) {
throw new Error("Unable to initialize WebGL. Your browser or machine may not support it.");
}
//---Got gl
return gl;
}
static getProgram(gl :WebGLRenderingContext,vshader:WebGLShader, fshader :WebGLShader) :WebGLProgram {
const pgm = gl.createProgram();
if (pgm == null){throw new Error("failed to create program");}
//-----------
gl.attachShader(pgm, vshader);
gl.attachShader(pgm, fshader);
//-------------
// pgm.vertexPosAttrib = gl.getAttribLocation( pgm , 'pos');
// this.gl.useProgram(this.program);
return pgm;
}
static getBuffer(gl :WebGLRenderingContext):WebGLBuffer{
let b = gl.createBuffer();
if (b == null){throw("failed to create buffer");}
return b;
}
static createShader(gl :WebGLRenderingContext, shaderSource :string, shaderType:number):WebGLShader {
var shader = gl.createShader(shaderType);
if (shader == null){
throw new Error("shaders could not be created");
}
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
let compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
// There are errors, so display them
var errors = gl.getShaderInfoLog(shader);
console.log('Failed to compile with these errors:' + "type:" + shaderType, errors );
}
return shader;
}
static bindBuffer(gl :WebGLRenderingContext,buff :WebGLBuffer,buffData :number[]){
gl.bindBuffer(gl.ARRAY_BUFFER, buff);
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(buffData),
gl.STATIC_DRAW);
}
static linkNuseProgram(gl :WebGLRenderingContext,prgrm :WebGLProgram){
gl.linkProgram(prgrm);
gl.useProgram(prgrm);
}
static clear(gl :WebGLRenderingContext,r:number=0,g:number=0,b:number=0,a:number=1){
gl.clearColor(r,g,b,a);
gl.clear(gl.COLOR_BUFFER_BIT);
}
////////////////////////////////////////////////////
static setAttribute(gl :WebGLRenderingContext,nameStr :string,programe :WebGLProgram,numberOfComps :number,stride:number, offset :number=0){
const vertexPosAttrib = gl.getAttribLocation( programe, `${nameStr}`);
gl.enableVertexAttribArray( vertexPosAttrib);
gl.vertexAttribPointer(
vertexPosAttrib, //index
numberOfComps, //number of components =2 x and y
gl.FLOAT, //data type
false, //normalized
stride , //stride - the comple vertex row bytes
offset //offset = 0
);
}
///////////////////////////////////////////////
}
Anf finally here is the picture of the triangle
Tried all the examples and help that I could find on the internet including re-learning the math
Since GL's drawing-area is stretched to fit the viewport, you need to take into account aspect ratio of the view in gl_Position calculation.
For example, appending aspectRatio parameter to the vertex shader,
uniform float aspectRatio;
:
gl_Position = vec4(
(translateX + (a_pos.x * cos(angle) - a_pos.y * sin(angle))) / aspectRatio,
translateY + (a_pos.x * sin(angle) + a_pos.y * cos(angle)),
1.0,
1.0 );
pass the viewport aspect-ratio as follows.
const viewport = gl.getParameter(gl.VIEWPORT); // [x, y, width, height]
const aspectRatioLoc = gl.getUniformLocation(programe, "aspectRatio");
gl.uniform1f(aspectRatioLoc, viewport[2] / viewport[3]);

How to get the 2d dimensions of the object being drawn for hit test on webgl after model view transform

I follow webgl fundamentals and draw 2d object and use matrices to scale vertices and render.
Before render I pass width/height that set as vertices to render a quad. This defines the size of the object. But in the vertex shader I apply transformation to these vertices like so:
in vec2 aPosition;
in vec2 aTexCoord;
out vec2 vQuadCoord;
uniform mat3 uMatrix;
void main() {
vec2 position = (uMatrix * vec3(aPosition, 1)).xy;
vQuadCoord = aTexCoord;
gl_Position = vec4(position, 0, 1);
}
This matrix controls translate/rotate/scale of the object. After render, I want to know the bounds of this object. But especially after scaling I can't know the bounds. If I translate this object (with matrices) at x,y it's position is known, but if I scale this object, x is shifted to the left, by unkown amount. webgl fundamentals don't mention about this topic, what is a good approach to detect the bounds of the object and transform precisely because I also have problems with the pivot, i might ask as another question.
You need to convert the mouse coordinates to clip space and then multiply them by the inverse of the matrix. this will give you mouse cooordinates that are relative to the values of aPosition.
After that it's up to you. If the values (the vertices) fed to aPosition are a rectangle than you can just check the transformed point against that rectangle. If they are a more complicated shape like a star then you'll need to make your own function to do point in star or point in triangle and check each triangle but at least after the transformation the mouse position is in coordinates relative to your vertices. You could also compute at init time the bounding box of the vertices and use that to test against the transformed point.
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert('need WebGL2');
}
const vs = `#version 300 es
in vec2 aPosition;
uniform mat3 uMatrix;
void main() {
vec2 position = (uMatrix * vec3(aPosition, 1)).xy;
gl_Position = vec4(position, 0, 1);
}
`;
const fs = `#version 300 es
precision mediump float;
uniform vec4 color;
out vec4 outColor;
void main() {
outColor = color;
}
`;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create a quad that starts at 0,0 and is 20 units wide and 10 tall
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
aPosition: {
numComponents: 2,
data: [
0, 0,
0, 10,
20, 0,
20, 0,
0, 10,
20, 10,
],
}
});
const vao = twgl.createVAOFromBufferInfo(gl, programInfo, bufferInfo);
let mouseClipX = 0;
let mouseClipY = 0;
const infoElem = document.querySelector('#info');
function render(time) {
t = time / 1000;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfo.program);
gl.bindVertexArray(vao);
let mat = m3.projection(gl.canvas.width, gl.canvas.height);
mat = m3.translate(
mat,
150 + Math.sin(t * 0.1) * 100,
75 + Math.cos(t * 0.2) * 50);
mat = m3.rotate(mat, t * 0.3);
mat = m3.scale(
mat,
2 + Math.sin(t * 0.4) * 0.5,
2 + Math.cos(t * 0.5) * 0.5);
// convert clipspace mouse to aPosition relative values
// 'mat' takes aPosition and converts to clip space
// so the inverse of 'mat' would take clip space and
// convert back to aPosition space.
const invMat = m3.inverse(mat);
const p = m3.transformPoint(invMat, [mouseClipX, mouseClipY]);
// now check in aPosition space. It's a 20x10 rect starting at 0,0 so
const inbox = p[0] >= 0 && p[0] < 20 &&
p[1] >= 0 && p[1] < 10;
twgl.setUniforms(programInfo, {
uMatrix: mat,
color: inbox ? [1, 0, 0, 1] : [0, 0, 1, 1],
});
twgl.drawBufferInfo(gl, bufferInfo);
infoElem.textContent = inbox ? 'mouse in rect' : 'no hit';
requestAnimationFrame(render);
}
requestAnimationFrame(render);
gl.canvas.addEventListener('mousemove', (event) => {
// convert canvas relative mouse coordinates to clip space
mouseClipX = (event.offsetX / gl.canvas.clientWidth ) * 2 - 1;
mouseClipY = (event.offsetY / gl.canvas.clientHeight) * -2 + 1; // note we flip Y
});
}
main();
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://webgl2fundamentals.org/webgl/resources/m3.js"></script>
<canvas></canvas>
<pre id="info"></pre>

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);
}

Chroma key Fragment Shader fails to find the color

I'm trying to write a fragment-shader that functions as a chroma-key filter for a specific color (for example make all pixels with a specific green transparent).
The shader I'm writing is for use in WebGL trough PIXI.js.
JSFiddle: https://jsfiddle.net/IbeVanmeenen/hexec6eg/14/
So far, I wrote this code for the shader, based on the shader I've found here.
varying vec2 vTextureCoord;
uniform float thresholdSensitivity;
uniform float smoothing;
uniform vec3 colorToReplace;
uniform sampler2D uSampler;
void main() {
vec4 textureColor = texture2D(uSampler, vTextureCoord);
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
float maskCr = 0.7132 * (colorToReplace.r - maskY);
float maskCb = 0.5647 * (colorToReplace.b - maskY);
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
float Cr = 0.7132 * (textureColor.r - Y);
float Cb = 0.5647 * (textureColor.b - Y);
float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
gl_FragColor = vec4(textureColor.rgb, textureColor.a * blendValue);
}
Now, when I define and test this, nothing happens.
The problem lies with the shader, because the other filters I tried work.
The color I use for the test is rgb(85, 249, 44).
The Full code for the shader with PIXI is below:
function ChromaFilter() {
const vertexShader = null;
const fragmentShader = [
"varying vec2 vTextureCoord;",
"uniform float thresholdSensitivity;",
"uniform float smoothing;",
"uniform vec3 colorToReplace;",
"uniform sampler2D uSampler;",
"void main() {",
"vec4 textureColor = texture2D(uSampler, vTextureCoord);",
"float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;",
"float maskCr = 0.7132 * (colorToReplace.r - maskY);",
"float maskCb = 0.5647 * (colorToReplace.b - maskY);",
"float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;",
"float Cr = 0.7132 * (textureColor.r - Y);",
"float Cb = 0.5647 * (textureColor.b - Y);",
"float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));",
"gl_FragColor = vec4(textureColor.rgb, textureColor.a * blendValue);",
"}"
].join('\n');
let uniforms = {};
PIXI.Filter.call(this,
vertexShader,
fragmentShader,
uniforms
);
this.uniforms.thresholdSensitivity = 0.4;
this.uniforms.smoothing = 0.1;
this.uniforms.colorToReplace = [0.33, 0.97, 0.17];
this.glShaderKey = 'chromakey';
}
ChromaFilter.prototype = Object.create(PIXI.Filter.prototype);
ChromaFilter.prototype.constructor = ChromaFilter;
This is applied to the video-sprite like this:
videoBase = new PIXI.VideoBaseTexture(videoLoaderVid);
videoBase.on('loaded', () => {
video = videoBase.source;
video.volume = 0;
video.pause();
video.currentTime = 0;
videoTexture = new PIXI.Texture(videoBase);
videoSprite = new PIXI.Sprite(videoTexture);
const filter = new ChromaFilter();
videoSprite.filters = [filter];
resolve();
});
And PIXI is set up like this:
stage = new PIXI.Container();
renderer = PIXI.autoDetectRenderer(720, 720, {
preserveDrawingBuffer: true,
clearBeforeRender: true
});
canvasContainer.appendChild(renderer.view);
The video-sprite sits on it's own DisplayObjectContainer and is displayed above another DisplayObjectContainer (hence the need for a chroma-filter)
UPDATE:
The fixed shader can be found here:
https://gist.github.com/IbeVanmeenen/d4f5225ad7d2fa54fabcc38d740ba30e
And a fixed demo can be found here:
https://jsfiddle.net/IbeVanmeenen/hexec6eg/17/
The shader is fine, the problem is that uniforms (colorToReplace, thresholdSensitivity and smoothing) aren't passed, they're all set to 0s. By blind luck I've found that to fix that you need to remove third parameter you're passing to PIXI.Filter constructor:
/* ... */
PIXI.Filter.call(this, vertexShader, fragmentShader) // no uniforms param here
/* ... */
PS. You haven't answer in chat, so I'm posting my findings here.

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)

Categories

Resources