Phong and Gouraud Shading WebGL - javascript

I read that that in Gouraud Shading, the color for the fragment is computed in the Vertex Shader. Whereas, in Phong Shading, the color for the fragment is computed in the Fragment Shader.
In this implementation , which of these are we using ?
I did not understand exactly the difference between them.
Could anyone help me ? Thanks
<button id = "ButtonX">Rotate X</button>
<button id = "ButtonY">Rotate Y</button>
<button id = "ButtonZ">Rotate Z</button>
<button id = "ButtonT">Toggle Rotation</button>
<button id="Direction">Change Direction</button>
<button id="OrthoPersp">Change Ortho/Persp</button>
<div>Traslation on X <input id="slideX" type="range"
min="-1" max="1" step="0.1" value="0" />
</div>
<div>Traslation on Y <input id="slideY" type="range"
min="-1" max="1" step="0.1" value="0" />
</div>
<div>Traslation on Z <input id="slideZ" type="range"
min="-1" max="1" step="0.1" value="0" />
</div>
<div>Scaling on X <input id="ScalingX" type="range"
min="0" max="1" step="0.1" value="0" />
</div>
<div>Scaling on Y <input id="ScalingY" type="range"
min="0" max="1" step="0.1" value="0" />
</div>
<div>Scaling on Z <input id="ScalingZ" type="range"
min="0" max="1" step="0.1" value="0" />
</div>
<div>
zNear Min<input id="zNearSlider" type="range" min="0.00" max="2.8" step="0.1" value="0.3">
Max
</div>
<div>
zFar Min<input id="zFarSlider" type="range" min="3" max="10" step="3.0" value="3">
Max
</div>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
//uniform vec3 theta;
// Point 2 -> Move the matrices
// Per spostare le matrici le abbiamo dovuto dichiarare nel file GLSL come uniform
// le matrici rx ry e rz sono rispettivamente le matrici di rotazione sugli assi
uniform mat4 rx;
uniform mat4 ry;
uniform mat4 rz;
// Points 3 -> Traslation Matrix
uniform mat4 traslation;
// Points 3 -> Scaling Matrix
uniform mat4 scaling;
//Point 4 -> MV and P matrices
uniform mat4 modelView;
uniform mat4 projection;
//Poinit 6 -> Light Source
attribute vec4 vNormal;
uniform vec4 ambientProduct, diffuseProduct, specularProduct;
uniform vec4 lightPosition;
uniform float shininess;
void main()
{
// Compute the sines and cosines of theta for each of
// the three axes in one computation.
//vec3 angles = radians( theta );
//vec3 c = cos( angles );
//vec3 s = sin( angles );
// Remember: the matrices are column-major
/*
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
-s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
*/
//fColor = vColor;
// ORDINE : scaling -> rotazione -> traslation
//gl_Position = projection*modelView*scaling *rz * ry * rx * traslation *vPosition ;
//gl_Position.z = -gl_Position.z;
//Point 6
vec3 pos = -(modelView * vPosition).xyz;
vec3 light = lightPosition.xyz;
vec3 L = normalize( light - pos );
vec3 E = normalize( -pos );
vec3 H = normalize( L + E );
vec3 N = normalize( (modelView*vNormal).xyz);
vec4 ambient = ambientProduct;
float Kd = max( dot(L, N), 0.0 );
vec4 diffuse = Kd*diffuseProduct;
float Ks = pow( max(dot(N, H), 0.0), shininess );
vec4 specular = Ks * specularProduct;
if( dot(L, N) < 0.0 ) {
specular = vec4(0.0, 0.0, 0.0, 1.0);
}
fColor = ambient + diffuse + specular;
fColor.a = 1.0;
gl_Position = projection*modelView*scaling *rz * ry * rx * traslation *vPosition ;
gl_Position.z = -gl_Position.z;
// *******************
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
void
main()
{
gl_FragColor = fColor;
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="Homework1.js"></script>
<body>
<canvas id="gl-canvas" width="1024" height="1024">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>

I did not understand exactly the difference between them. Could anyone help me ?
The technique used in the code snippet of the question is Gouraud Shading.
In common Phong shading means the technique, which does the light calculations per fragment, in compare at Gouraud Shading, the light calculations ar done per vertex.
This means that at Phong shading the light calcualtions ar done in the fragment shader (Not to be confused with Phong reflection model).
Phong shading:
At Gouraud Shading the light calculations are done in the vertex shader, for the corners of the primitives (corners of the triangles). The calculated light is (either in a perspective correct manner or linearly) interpolated for all the fragments covered by the primitive, according to the Barycentric coordinate. This increases the performance, but gives a big loss of quality, especially on large primitives and strong specular highlights.
Gouraud Shading:
Note, the light is not linear distributed on a surface. If the light is only calculated for some samples and interpolated in between them, this causes flat stains.
See the example, which compares the 2 techniques:
(function loadscene() {
var resize, gl, gouraudDraw, phongDraw, vp_size;
var bufSphere = {};
function render(delteMS){
var shading = document.getElementById( "shading" ).value;
var shininess = document.getElementById( "shininess" ).value;
var ambientCol = [0.2, 0.2, 0.2];
var diffuseCol = [0.6, 0.6, 0.6];
var specularCol = [0.8, 0.8, 0.8];
Camera.create();
Camera.vp = vp_size;
gl.enable( gl.DEPTH_TEST );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
//gl.frontFace(gl.CW);
gl.frontFace(gl.CCW);
var progDraw = shading == 0 ? gouraudDraw : phongDraw;;
// set up draw shader
ShaderProgram.Use( progDraw.prog );
ShaderProgram.SetUniformM44( progDraw.prog, "u_projectionMat44", Camera.Perspective() );
ShaderProgram.SetUniformM44( progDraw.prog, "u_viewMat44", Camera.LookAt() );
ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.lightDir", [-1.0, -0.5, -2.0] )
ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.ambient", ambientCol )
ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.diffuse", diffuseCol )
ShaderProgram.SetUniformF3( progDraw.prog, "u_lightSource.specular", specularCol )
ShaderProgram.SetUniformF1( progDraw.prog, "u_lightSource.shininess", shininess )
var modelMat = IdentityMat44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShaderProgram.SetUniformM44( progDraw.prog, "u_modelMat44", modelMat );
// draw scene
VertexBuffer.Draw( bufSphere );
requestAnimationFrame(render);
}
function resize() {
//vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
vp_size = [window.innerWidth, window.innerHeight]
canvas.width = vp_size[0];
canvas.height = vp_size[1];
gl.viewport( 0, 0, vp_size[0], vp_size[1] );
}
function initScene() {
canvas = document.getElementById( "canvas");
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return null;
gouraudDraw = {}
gouraudDraw.prog = ShaderProgram.Create(
[ { source : "gouraud-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "gouraud-shader-fs", stage : gl.FRAGMENT_SHADER }
],
[ "u_projectionMat44", "u_viewMat44", "u_modelMat44",
"u_lightSource.lightDir", "u_lightSource.ambient", "u_lightSource.diffuse", "u_lightSource.specular", "u_lightSource.shininess", ] );
if ( gouraudDraw.prog == 0 )
return;
gouraudDraw.inPos = gl.getAttribLocation( gouraudDraw.prog, "inPos" );
gouraudDraw.inNV = gl.getAttribLocation( gouraudDraw.prog, "inNV" );
gouraudDraw.inCol = gl.getAttribLocation( gouraudDraw.prog, "inCol" );
phongDraw = {}
phongDraw.prog = ShaderProgram.Create(
[ { source : "phong-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "phong-shader-fs", stage : gl.FRAGMENT_SHADER }
],
[ "u_projectionMat44", "u_viewMat44", "u_modelMat44",
"u_lightSource.lightDir", "u_lightSource.ambient", "u_lightSource.diffuse", "u_lightSource.specular", "u_lightSource.shininess", ] );
if ( phongDraw.prog == 0 )
return;
phongDraw.inPos = gl.getAttribLocation( phongDraw.prog, "inPos" );
phongDraw.inNV = gl.getAttribLocation( phongDraw.prog, "inNV" );
phongDraw.inCol = gl.getAttribLocation( phongDraw.prog, "inCol" );
// create cube
var layer_size = 16, circum_size = 32;
var rad_circum = 1.0;
var rad_tube = 0.5;
var sphere_pts = [];
var sphere_nv = [];
var sphere_col = [];
sphere_pts.push( 0.0, 0.0, -2.0 );
sphere_nv.push( 0.0, 0.0, -1.0 );
sphere_col.push( 0.8, 0.6, 0.3 );
for ( var i_l = 1; i_l < layer_size; ++ i_l ) {
var angH = (1.0 - i_l / layer_size) * Math.PI;
var h = Math.cos( angH );
var r = Math.sin( angH );
for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
var circumX = Math.cos(2 * Math.PI * i_c / circum_size);
var circumY = Math.sin(2 * Math.PI * i_c / circum_size);
sphere_pts.push( r * circumX * 2.0, r * circumY * 2.0, h * 2.0 );
sphere_nv.push( r * circumX, r * circumY, h );
sphere_col.push( 0.8, 0.6, 0.3 );
}
}
sphere_pts.push( 0.0, 0.0, 2.0 );
sphere_nv.push( 0.0, 0.0, 1.0 );
sphere_col.push( 0.75, 0.75, 0.75 );
var sphere_inx = [];
for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
sphere_inx.push( i_c+1, 0, (i_c+1) % circum_size + 1 )
}
for ( var i_l = 0; i_l < layer_size-2; ++ i_l ) {
var l1 = i_l * circum_size + 1;
var l2 = (i_l+1) * circum_size + 1
for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
var i_n = (i_c+1) % circum_size;
sphere_inx.push( l1+i_c, l1+i_n, l2+i_c, l1+i_n, l2+i_n, l2+i_c );
}
}
for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
var i_start = 1 + (layer_size-2) * circum_size;
var i_n = (i_c+1) % circum_size;
sphere_inx.push( i_start + i_c, i_start + i_n, sphere_pts.length/3-1 );
}
bufSphere = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : gouraudDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : gouraudDraw.inNV },
{ data : sphere_col, attrSize : 3, attrLoc : gouraudDraw.inCol } ],
sphere_inx );
window.onresize = resize;
resize();
requestAnimationFrame(render);
}
function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}
glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );
function IdentityMat44() {
var m = new glArrayType(16);
m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 0;
m[4] = 0; m[5] = 1; m[6] = 0; m[7] = 0;
m[8] = 0; m[9] = 0; m[10] = 1; m[11] = 0;
m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
return m;
};
function RotateAxis(matA, angRad, axis) {
var aMap = [ [1, 2], [2, 0], [0, 1] ];
var a0 = aMap[axis][0], a1 = aMap[axis][1];
var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
var matB = new glArrayType(16);
for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
for ( var i = 0; i < 3; ++ i ) {
matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
}
return matB;
}
function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; }
function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
function Normalize( v ) {
var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
return [ v[0] / len, v[1] / len, v[2] / len ];
}
var Camera = {};
Camera.create = function() {
this.pos = [0, 3, 0.0];
this.target = [0, 0, 0];
this.up = [0, 0, 1];
this.fov_y = 90;
this.vp = [800, 600];
this.near = 0.5;
this.far = 100.0;
}
Camera.Perspective = function() {
var fn = this.far + this.near;
var f_n = this.far - this.near;
var r = this.vp[0] / this.vp[1];
var t = 1 / Math.tan( Math.PI * this.fov_y / 360 );
var m = IdentityMat44();
m[0] = t/r; m[1] = 0; m[2] = 0; m[3] = 0;
m[4] = 0; m[5] = t; m[6] = 0; m[7] = 0;
m[8] = 0; m[9] = 0; m[10] = -fn / f_n; m[11] = -1;
m[12] = 0; m[13] = 0; m[14] = -2 * this.far * this.near / f_n; m[15] = 0;
return m;
}
Camera.LookAt = function() {
var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] );
var mx = Normalize( Cross( this.up, mz ) );
var my = Normalize( Cross( mz, mx ) );
var tx = Dot( mx, this.pos );
var ty = Dot( my, this.pos );
var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos );
var m = IdentityMat44();
m[0] = mx[0]; m[1] = my[0]; m[2] = mz[0]; m[3] = 0;
m[4] = mx[1]; m[5] = my[1]; m[6] = mz[1]; m[7] = 0;
m[8] = mx[2]; m[9] = my[2]; m[10] = mz[2]; m[11] = 0;
m[12] = tx; m[13] = ty; m[14] = tz; m[15] = 1;
return m;
}
var ShaderProgram = {};
ShaderProgram.Create = function( shaderList ) {
var shaderObjs = [];
for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
if ( shderObj == 0 )
return 0;
shaderObjs.push( shderObj );
}
var progObj = this.LinkProgram( shaderObjs )
if ( progObj != 0 ) {
progObj.attribIndex = {};
var noOfAttributes = gl.getProgramParameter( progObj, gl.ACTIVE_ATTRIBUTES );
for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
var name = gl.getActiveAttrib( progObj, i_n ).name;
progObj.attribIndex[name] = gl.getAttribLocation( progObj, name );
}
progObj.unifomLocation = {};
var noOfUniforms = gl.getProgramParameter( progObj, gl.ACTIVE_UNIFORMS );
for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
var name = gl.getActiveUniform( progObj, i_n ).name;
progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
}
}
return progObj;
}
ShaderProgram.AttributeIndex = function( progObj, name ) { return progObj.attribIndex[name]; }
ShaderProgram.UniformLocation = function( progObj, name ) { return progObj.unifomLocation[name]; }
ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); }
ShaderProgram.SetUniformI1 = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1i( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniformF1 = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1f( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniformF2 = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformF3 = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform3fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformF4 = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform4fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformM33 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix3fv( progObj.unifomLocation[name], false, mat ); }
ShaderProgram.SetUniformM44 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
ShaderProgram.CompileShader = function( source, shaderStage ) {
var shaderScript = document.getElementById(source);
if (shaderScript)
source = shaderScript.text;
var shaderObj = gl.createShader( shaderStage );
gl.shaderSource( shaderObj, source );
gl.compileShader( shaderObj );
var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
return status ? shaderObj : null;
}
ShaderProgram.LinkProgram = function( shaderObjs ) {
var prog = gl.createProgram();
for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
gl.attachShader( prog, shaderObjs[i_sh] );
gl.linkProgram( prog );
status = gl.getProgramParameter( prog, gl.LINK_STATUS );
if ( !status ) alert("Could not initialise shaders");
gl.useProgram( null );
return status ? prog : null;
}
var VertexBuffer = {};
VertexBuffer.Create = function( attributes, indices ) {
var buffer = {};
buffer.buf = [];
buffer.attr = []
for ( var i = 0; i < attributes.length; ++ i ) {
buffer.buf.push( gl.createBuffer() );
buffer.attr.push( { size : attributes[i].attrSize, loc : attributes[i].attrLoc } );
gl.bindBuffer( gl.ARRAY_BUFFER, buffer.buf[i] );
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( attributes[i].data ), gl.STATIC_DRAW );
}
buffer.inx = gl.createBuffer();
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buffer.inx );
gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW );
buffer.inxLen = indices.length;
gl.bindBuffer( gl.ARRAY_BUFFER, null );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
return buffer;
}
VertexBuffer.Draw = function( bufObj ) {
for ( var i = 0; i < bufObj.buf.length; ++ i ) {
gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.buf[i] );
gl.vertexAttribPointer( bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( bufObj.attr[i].loc );
}
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
gl.drawElements( gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0 );
for ( var i = 0; i < bufObj.buf.length; ++ i )
gl.disableVertexAttribArray( bufObj.attr[i].loc );
gl.bindBuffer( gl.ARRAY_BUFFER, null );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
initScene();
})();
<style>
html,body {
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
}
#gui {
position : absolute;
top : 0;
left : 0;
}
</style>
<script id="gouraud-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec3 inPos;
attribute vec3 inNV;
attribute vec3 inCol;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
struct TLightSource
{
vec3 lightDir;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform TLightSource u_lightSource;
vec3 Light( vec3 eyeV, vec3 N )
{
vec3 lightCol = u_lightSource.ambient;
vec3 L = normalize( -u_lightSource.lightDir );
float NdotL = max( 0.0, dot( N, L ) );
lightCol += NdotL * u_lightSource.diffuse;
vec3 H = normalize( eyeV + L );
float NdotH = max( 0.0, dot( N, H ) );
float kSpecular = ( u_lightSource.shininess + 2.0 ) * pow( NdotH, u_lightSource.shininess ) / ( 2.0 * 3.14159265 );
lightCol += kSpecular * u_lightSource.specular;
return lightCol;
}
void main()
{
vec3 modelNV = mat3( u_modelMat44 ) * normalize( inNV );
vertNV = mat3( u_viewMat44 ) * modelNV;
vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
vec4 viewPos = u_viewMat44 * modelPos;
vertPos = viewPos.xyz / viewPos.w;
vec3 eyeV = normalize( -vertPos );
vec3 normalV = normalize( vertNV );
vertCol = inCol * Light( eyeV, normalV );
gl_Position = u_projectionMat44 * viewPos;
}
</script>
<script id="gouraud-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
void main()
{
gl_FragColor = vec4( vertCol, 1.0 );
}
</script>
<script id="phong-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec3 inPos;
attribute vec3 inNV;
attribute vec3 inCol;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
void main()
{
vec3 modelNV = mat3( u_modelMat44 ) * normalize( inNV );
vertNV = mat3( u_viewMat44 ) * modelNV;
vertCol = inCol;
vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
vec4 viewPos = u_viewMat44 * modelPos;
vertPos = viewPos.xyz / viewPos.w;
gl_Position = u_projectionMat44 * viewPos;
}
</script>
<script id="phong-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
struct TLightSource
{
vec3 lightDir;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform TLightSource u_lightSource;
vec3 Light( vec3 eyeV, vec3 N )
{
vec3 lightCol = u_lightSource.ambient;
vec3 L = normalize( -u_lightSource.lightDir );
float NdotL = max( 0.0, dot( N, L ) );
lightCol += NdotL * u_lightSource.diffuse;
vec3 H = normalize( eyeV + L );
float NdotH = max( 0.0, dot( N, H ) );
float kSpecular = ( u_lightSource.shininess + 2.0 ) * pow( NdotH, u_lightSource.shininess ) / ( 2.0 * 3.14159265 );
lightCol += kSpecular * u_lightSource.specular;
return lightCol;
}
void main()
{
vec3 eyeV = normalize( -vertPos );
vec3 normalV = normalize( vertNV );
vec3 color = vertCol * Light( eyeV, normalV );
gl_FragColor = vec4( color, 1.0 );
}
</script>
<form id="gui" name="inputs"><table><tr>
<td><font color= #CCF>Shading:</font></td>
<td><select id="shading">>
<option value="0">Gouraud</option>
<option value="1">Phong</option>
</select></td>
</tr><tr>
<td><font color= #CCF>Shininess:</font></td>
<td><input type="range" id="shininess" min="0" max="100" value="20"/></td>
</tr></table></form>
<canvas id="canvas" style="border: none;" width="100%" height="100%"></canvas>

Related

React Three Fiber ShaderMaterial animation problem

I've been trying to find where I made a mistake maybe 30 times, but I couldn't find it. There is no problem in the view, but the animation does not work in the Component.
The view works well, but the animation needs to work in the code I wrote in the fragment. The link I referenced is
https://www.youtube.com/watch?v=3krH52AhPqk.
import React, { Suspense, useMemo, useRef } from "react";
import * as THREE from "three";
import { Canvas, useFrame,extend } from "#react-three/fiber";
import { OrbitControls } from "#react-three/drei";
import fragmentShader from "./shader/fragment.js";
import vertexShader from "./shader/vertex.js";
import fragment1 from "./shader/shaderSun/fragment.js";
import vertex1 from "./shader/shaderSun/vertex.js";
// function Perlin(props) {
// return (
// <mesh {...props}>
// <sphereBufferGeometry />
// <meshPhysicalMaterial />
// <shaderMaterial
// attach="material"
// args={[
// {
// uniforms: [1.1, 30, 30],
// vertexShader: vertex1,
// fragmentShader: fragment1,
// },
// ]}
// side={THREE.DoubleSide}
// />
// </mesh>
// );
// }
class CustomMaterial extends THREE.ShaderMaterial {
constructor() {
super({
uniforms: { time:{value:0},resolution:{value:new THREE.Vector4()} },
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide
})
}
}
extend({ CustomMaterial })
function Sphere(props) {
// const data = useMemo(() => ({ fragment }));
const myMesh = React.useRef();
// useFrame(({ clock }) => {
// myMesh.current.rotation.z += 0.1;
// });
// const data = useMemo(
// () => ({
// uniforms: {},
// fragmentShader,
// vertexShader,
// uPerlin: { value: null },
// }),
// []
// );
return (
<mesh {...props} ref={myMesh}>
<sphereBufferGeometry args={[1, 30, 30]} />
{/*
<Perlin /> */}
<customMaterial attach="material" />
</mesh>
);
}
function index() {
return (
<Canvas >
{" "}
{/* <spotLight
intensity={0.5}
angle={0.2}
penumbra={1}
position={[5, -25, 10]}
/>{" "} */}
<Suspense fallback={null}>
<Sphere />
{/* <Perlin /> */}
</Suspense>
<OrbitControls />
</Canvas>
);
}
export default index;
fragmentShader
const fragment = `
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : ijm
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
uniform float time;
uniform float progress;
uniform sampler2D texture1;
uniform vec4 resolution;
varying vec2 vUv;
varying vec3 vPosition;
float PI= 3.141592653589793238;
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
float mod289(float x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+1.0)*x);
}
float permute(float x) {
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float taylorInvSqrt(float r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
vec4 grad4(float j, vec4 ip)
{
const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
vec4 p,s;
p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
s = vec4(lessThan(p, vec4(0.0)));
p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;
return p;
}
// (sqrt(5) - 1)/4 = F4, used once below
#define F4 0.309016994374947451
float snoise(vec4 v)
{
const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4
0.276393202250021, // 2 * G4
0.414589803375032, // 3 * G4
-0.447213595499958); // -1 + 4 * G4
// First corner
vec4 i = floor(v + dot(v, vec4(F4)) );
vec4 x0 = v - i + dot(i, C.xxxx);
// Other corners
// Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
vec4 i0;
vec3 isX = step( x0.yzw, x0.xxx );
vec3 isYZ = step( x0.zww, x0.yyz );
// i0.x = dot( isX, vec3( 1.0 ) );
i0.x = isX.x + isX.y + isX.z;
i0.yzw = 1.0 - isX;
// i0.y += dot( isYZ.xy, vec2( 1.0 ) );
i0.y += isYZ.x + isYZ.y;
i0.zw += 1.0 - isYZ.xy;
i0.z += isYZ.z;
i0.w += 1.0 - isYZ.z;
// i0 now contains the unique values 0,1,2,3 in each channel
vec4 i3 = clamp( i0, 0.0, 1.0 );
vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );
// x0 = x0 - 0.0 + 0.0 * C.xxxx
// x1 = x0 - i1 + 1.0 * C.xxxx
// x2 = x0 - i2 + 2.0 * C.xxxx
// x3 = x0 - i3 + 3.0 * C.xxxx
// x4 = x0 - 1.0 + 4.0 * C.xxxx
vec4 x1 = x0 - i1 + C.xxxx;
vec4 x2 = x0 - i2 + C.yyyy;
vec4 x3 = x0 - i3 + C.zzzz;
vec4 x4 = x0 + C.wwww;
// Permutations
i = mod289(i);
float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
vec4 j1 = permute( permute( permute( permute (
i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
+ i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
+ i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
+ i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
// Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
// 7*7*6 = 294, which is close to the ring size 17*17 = 289.
vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;
vec4 p0 = grad4(j0, ip);
vec4 p1 = grad4(j1.x, ip);
vec4 p2 = grad4(j1.y, ip);
vec4 p3 = grad4(j1.z, ip);
vec4 p4 = grad4(j1.w, ip);
// Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
p4 *= taylorInvSqrt(dot(p4,p4));
// Mix contributions from the five corners
vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0);
m0 = m0 * m0;
m1 = m1 * m1;
return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
+ dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;
}
void main(){
// vec4 p=vec4(vPosition*5.,time*0.2);
// float noisy=fbm(p);
// vec4 p1=vec4(vPosition*2.,time*0.02);
// float spots=max(snoise(p1),0.);
// gl_FragColor=vec4(noisy);
// gl_FragColor*=mix(1.,spots,0.7);
// // gl_FragColor = vec4(vUv,0.0,1.);
// // gl_FragColor=vec4(vPosition,1.);
float noisy=snoise(vec4(vPosition*5.,time));
gl_FragColor=vec4(vUv,0.0,1.);
gl_FragColor=vec4(noisy);
// gl_FragColor=vec4(vPosition,1.);
}
`;
export default fragment;
Vertex
const vertex = `
uniform float time;
varying vec2 vUv;
varying vec3 vPosition;
varying vec2 pixels;
float PI = 3.141592653589793238;
void main(){
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
}
`;
export default vertex;
Thank you in advance for your opinions.

how do i make the html code appear on top of the animation and not behind it

I tried putting this in CSS p{ z-index: 99 !important; } it didn't work
I Just want to make everything in html appear on top of the animation
here is the code::
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="container">
<p>hellpo</p>
</div>
<script id="vertex-shader" type="no-js">
void main() {
gl_Position = vec4( position, 1.0 );
}
</script>
<script id="fragment-shader" type="no-js">
uniform float iGlobalTime;
uniform vec2 iResolution;
const int NUM_STEPS = 8;
const float PI = 3.1415;
const float EPSILON = 1e-3;
float EPSILON_NRM = 0.1 / iResolution.x;
// sea variables
const int ITER_GEOMETRY = 3;
const int ITER_FRAGMENT = 5;
const float SEA_HEIGHT = 0.6;
const float SEA_CHOPPY = 1.0;
const float SEA_SPEED = 1.0;
const float SEA_FREQ = 0.16;
const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
float SEA_TIME = iGlobalTime * SEA_SPEED;
mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
mat3 fromEuler(vec3 ang) {
vec2 a1 = vec2(sin(ang.x),cos(ang.x));
vec2 a2 = vec2(sin(ang.y),cos(ang.y));
vec2 a3 = vec2(sin(ang.z),cos(ang.z));
mat3 m;
m[0] = vec3(
a1.y*a3.y+a1.x*a2.x*a3.x,
a1.y*a2.x*a3.x+a3.y*a1.x,
-a2.y*a3.x
);
m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
m[2] = vec3(
a3.y*a1.x*a2.x+a1.y*a3.x,
a1.x*a3.x-a1.y*a3.y*a2.x,
a2.y*a3.y
);
return m;
}
float hash( vec2 p ) {
float h = dot(p,vec2(127.1,311.7));
return fract(sin(h)*43758.5453123);
}
float noise( in vec2 p ) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return -1.0 + 2.0 * mix(
mix(
hash(i + vec2(0.0,0.0)
),
hash(i + vec2(1.0,0.0)), u.x),
mix(hash(i + vec2(0.0,1.0) ),
hash(i + vec2(1.0,1.0) ), u.x),
u.y
);
}
float diffuse(vec3 n,vec3 l,float p) {
return pow(dot(n,l) * 0.4 + 0.6,p);
}
float specular(vec3 n,vec3 l,vec3 e,float s) {
float nrm = (s + 8.0) / (3.1415 * 8.0);
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
}
vec3 getSkyColor(vec3 e) {
e.y = max(e.y, 0.0);
vec3 ret;
ret.x = pow(1.0 - e.y, 2.0);
ret.y = 1.0 - e.y;
ret.z = 0.6+(1.0 - e.y) * 0.4;
return ret;
}
float sea_octave(vec2 uv, float choppy) {
uv += noise(uv);
vec2 wv = 1.0 - abs(sin(uv));
vec2 swv = abs(cos(uv));
wv = mix(wv, swv, wv);
return pow(1.0 - pow(wv.x * wv.y, 0.65), choppy);
}
float map(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz;
uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_GEOMETRY; i++) {
d = sea_octave((uv + SEA_TIME) * freq, choppy);
d += sea_octave((uv - SEA_TIME) * freq, choppy);
h += d * amp;
uv *= octave_m;
freq *= 1.9;
amp *= 0.22;
choppy = mix(choppy, 1.0, 0.2);
}
return p.y - h;
}
float map_detailed(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz;
uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_FRAGMENT; i++) {
d = sea_octave((uv+SEA_TIME) * freq, choppy);
d += sea_octave((uv-SEA_TIME) * freq, choppy);
h += d * amp;
uv *= octave_m;
freq *= 1.9;
amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
vec3 getSeaColor(
vec3 p,
vec3 n,
vec3 l,
vec3 eye,
vec3 dist
) {
float fresnel = 1.0 - max(dot(n,-eye),0.0);
fresnel = pow(fresnel,3.0) * 0.65;
vec3 reflected = getSkyColor(reflect(eye,n));
vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
vec3 color = mix(refracted,reflected,fresnel);
float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
color += vec3(specular(n,l,eye,60.0));
return color;
}
// tracing
vec3 getNormal(vec3 p, float eps) {
vec3 n;
n.y = map_detailed(p);
n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
n.y = eps;
return normalize(n);
}
float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
float tm = 0.0;
float tx = 1000.0;
float hx = map(ori + dir * tx);
if(hx > 0.0) {
return tx;
}
float hm = map(ori + dir * tm);
float tmid = 0.0;
for(int i = 0; i < NUM_STEPS; i++) {
tmid = mix(tm,tx, hm/(hm-hx));
p = ori + dir * tmid;
float hmid = map(p);
if(hmid < 0.0) {
tx = tmid;
hx = hmid;
} else {
tm = tmid;
hm = hmid;
}
}
return tmid;
}
void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;
uv = uv * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float time = iGlobalTime * 0.3;
// ray
vec3 ang = vec3(
sin(time*3.0)*0.1,sin(time)*0.2+0.3,time
);
vec3 ori = vec3(0.0,3.5,time*5.0);
vec3 dir = normalize(
vec3(uv.xy,-2.0)
);
dir.z += length(uv) * 0.15;
dir = normalize(dir);
// tracing
vec3 p;
heightMapTracing(ori,dir,p);
vec3 dist = p - ori;
vec3 n = getNormal(
p,
dot(dist,dist) * EPSILON_NRM
);
vec3 light = normalize(vec3(0.0,1.0,0.8));
// color
vec3 color = mix(
getSkyColor(dir),
getSeaColor(p,n,light,dir,dist),
pow(smoothstep(0.0,-0.05,dir.y),0.3)
);
// post
gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0);
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="script.js"></script>
</body>
</html>
css
body {
overflow: hidden;
margin: 0;
}
p{ z-index: 99 !important; }
JavaScript
var container,
renderer,
scene,
camera,
mesh,
start = Date.now(),
fov = 30;
var clock = new THREE.Clock();
var timeUniform = {
iGlobalTime: {
type: 'f',
value: 0.1
},
iResolution: {
type: 'v2',
value: new THREE.Vector2()
}
};
timeUniform.iResolution.value.x = window.innerWidth;
timeUniform.iResolution.value.y = window.innerHeight;
window.addEventListener('load', function() {
container = document.getElementById('container');
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
fov,
window.innerWidth / window.innerHeight,
1,
10000
);
camera.position.x = 20;
camera.position.y = 10;
camera.position.z = 20;
camera.lookAt(scene.position);
scene.add(camera);
var axis = new THREE.AxisHelper(10);
scene.add (axis);
material = new THREE.ShaderMaterial({
uniforms: timeUniform,
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
var water = new THREE.Mesh(
new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight, 40), material
);
scene.add(water);
var geometry = new THREE.SphereGeometry( 10, 32, 32 );
var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
render();
});
window.addEventListener('resize',function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
function render() {
timeUniform.iGlobalTime.value += clock.getDelta();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
you can wrap ur animation in a div(your ThreeJs canvas). give the div and add this styles to it
position: absolute;
top: 0;
left: 0;
z-index: -1;
and make #container position: relative

How to add a displacement map to a point material in ThreeJS?

I'm new to ThreeJS and trying to create something like asteroids with textures made of particles (something like from this website https://staratlas.com/). Unfortunately, when I tried to apply a displacement map to a points material nothing worked. How can I resolve this issue?
const dispTexture = new THREE.TextureLoader().load('NormalMap.jpg');
const sphere = new THREE.Points(
new THREE.SphereGeometry(3, 32, 32),
new THREE.PointsMaterial({
size: 0.07,
color: 0xFF325F,
displacementMap: dispTexture,
displacementScale : 0.2,
})
);
scene.add(sphere);
The option, mentioned in comments, with the using of noise. An asteroid of 30K points. Added a simple lighting: the more light on a point, the bigger and brighter it is.
Just in case, codepen: https://codepen.io/prisoner849/pen/mdBzjBy
Maybe it'll be helpful for somebody:
body{
overflow: hidden;
margin: 0;
}
<script>
simplexNoise = `
// Simplex 3D Noise
// by Ian McEwan, Ashima Arts
//
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
float snoise(vec3 v){
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0. + 0.0 * C
vec3 x1 = x0 - i1 + 1.0 * C.xxx;
vec3 x2 = x0 - i2 + 2.0 * C.xxx;
vec3 x3 = x0 - 1. + 3.0 * C.xxx;
// Permutations
i = mod(i, 289.0 );
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients
// ( N*N points uniformly over a square, mapped onto an octahedron.)
float n_ = 1.0/7.0; // N=7
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
`;
</script>
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0xffffff)
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 0);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
//scene.add(new THREE.GridHelper());
let pts = new Array(30000).fill().map(p => {
return new THREE.Vector3().randomDirection().multiplyScalar(4);
});
let g = new THREE.BufferGeometry().setFromPoints(pts);
let u = {
time: {value: 0},
lightPos: {value: new THREE.Vector3()}
}
let m = new THREE.PointsMaterial({
size: 0.075,
color: 0x7fffff,
//map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png"),
onBeforeCompile: shader => {
shader.uniforms.lightPos = u.lightPos;
shader.vertexShader = `
uniform float time; // just the force of habit to add it :)
uniform vec3 lightPos;
varying float vShade;
${simplexNoise}
float turbulence( vec3 p ) {
float w = 100.0;
float t = -.5;
for (float f = 1.0 ; f <= 10.0 ; f++ ){
float power = pow( 2.0, f );
t += snoise( vec3( power * p ) ) / power ;
}
return t;
}
vec3 setFromSphericalCoords( float radius, float phi, float theta ) {
float sinPhiRadius = sin( phi ) * radius;
vec3 v = vec3( sinPhiRadius * sin( theta ), cos( phi ) * radius, sinPhiRadius * cos( theta ) );
return v;
}
vec2 setFromCartesianCoords( vec3 v ) {
float radius = sqrt( v.x * v.x + v.y * v.y + v.z * v.z );
float theta = 0.;
float phi = 0.;
if ( radius != 0. ) {
theta = atan( v.x, v.z );
phi = acos( clamp( v.y / radius, - 1., 1. ) );
}
return vec2(phi, theta);
}
vec3 getPoint(vec3 p){
vec3 n = normalize(p);
float s = turbulence(n * 0.5);
return p + n * s;
}
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
vec3 p0 = getPoint(position);
vec2 spherical = setFromCartesianCoords(position);
vec2 s = vec2(0.01, 0.);
vec3 p1 = setFromSphericalCoords(length(position), spherical.x + s.x, spherical.y + s.y);
vec3 p2 = setFromSphericalCoords(length(position), spherical.x + s.y, spherical.y + s.x);
p1 = getPoint(p1);
p2 = getPoint(p2);
vec3 nor = normalize(cross(p1 - p0, p2 - p0));
transformed = p0;
`
).replace(
`gl_PointSize = size;`,
`
vec3 lightDir = normalize(lightPos);
float shade = clamp(dot(nor, lightDir), 0., 1.);
vShade = shade;
gl_PointSize = size + (shade * size);`
);
console.log(shader.vertexShader);
shader.fragmentShader = `
varying float vShade;
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`
if(length(gl_PointCoord - 0.5) > 0.5) discard; // make'em round
float shade = vShade * 0.5 + 0.5;
vec4 diffuseColor = vec4( diffuse * shade, opacity );`
);
console.log(shader.fragmentShader);
}
});
let p = new THREE.Points(g, m);
scene.add(p);
let clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
let t = clock.getElapsedTime() * 0.5;
p.rotation.x = t * 0.271;
p.rotation.y = t * 0.314;
p.rotation.z = t * 0.158;
p.worldToLocal(u.lightPos.value.copy(light.position).add(p.position));
renderer.render(scene, camera);
})
</script>

THREE.JS | GLSL Set particle colour by scene texture

I have matrix of GLSL particles (THREE.Points), which have to be coloured by overall scene texture.
So, the result should be like this:
Yeah, I can create canvas and set buffer.color by ctx.getImageData(left, top, width, height), but is it possible to set it inside shader?
I have tried to get particle 'global' position, but without any success.
The output is:
var blob = "data:image/png;base64,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eG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0yOVQyMjozODowNCswMzowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDQtMjlUMjI6Mzg6MDQrMDM6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OGM0MWExZjUtMGE3NS1iNjRhLThlY2UtYTI0YjRlNjdlZmE3IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGU0ODQwM2UtNTYzNS00MmM4LTkxYzQtZjZlODIzMzk0MDg5IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9IkRpc3BsYXkiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjRlNDg0MDNlLTU2MzUtNDJjOC05MWM0LWY2ZTgyMzM5NDA4OSIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MjliNmNjMy01Y2Y3LTQzOWUtYTA1YS1lOTNhN2FjYzg2OWEiIHN0RXZ0OndoZW49IjIwMTktMDEtMjFUMTU6MzM6NDErMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTI5VDIyOjM4OjA0KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x4suaAAAA5FJREFUeJzt20uLHFUYxvHf1KgkKk4wMCEIUcGoMSAGyeBK8RLwsjFZ6CfwAuJ1bbI0++gmfgNduDOB6Ci6iYwKLtSxTSAmoHEGRm3BZBidlIszHXvavlR3ddWZrp4/NFRXV9V53rdP3d7LRJqmSuBa3Im7sBO3YBu2rP2+jD/wMy6ihh/xd9HCrinw2DvwKB7E/diDGzLu+xfm8TU+xywWCtBoooAZ8CyewZP++4fzsowTeB/vDemYGJ4DbsRLeAG3D+OAXTiH43gbl3IfLU3TvJ9X0zRdSMvn17Wxc+nPMwMewDHsz/0v5ONLvIIvBtk5GXDQN3FafOMJGk4Lmvqm3xlwHT7AU4MMVgIf4hBWsu7QjwN24lPhXr6RqeFh4XmiJ1kdcCvmMD24rlJZxAzO99owiwOm8T2259dVKku4R3BGR3o5YBJnFH9vL4pz2I3VThv0ugucNLrGE7Sf7LZBNwccxoGhyonDAcGWtnQ6Bfbi26IURWKvcC1bRycH1ITX1ypxRhub2p0Cz7fbsALsxoutK1tnwCTqsr+3jxqXMIV/GitaZ8Brqms8XC/YeJXWGbCEm0sUFIPfND3UNc+Ap1XfeIKNBxtfmh3wvwtEhXmusdA4BabwOyZiKSqZK8JMqDdmwOPGx3jCzH+isUAIX48bjxAcMCm8O48bM5icSNN0D74Rwl3jxAr2JULGZtyMJ9h8d4JdsZVEZFdidOJ8RTCdCFnacWVbgq2xVURk66CZoapwJcHl2CoispwIlRnjSj3RI3FQcRYTXIitIiIXEqEWJ3M2tUKs4IdEqMaajywmBvOoJULebC6ymBjMYbXxHDAbU0kkZtkMiV0NidVxKp6e0jkl2LwuKnw8jpYovNtY2EyMtPz4VrlaonC0+Uu75OifQg6tilzGTbokR1fxepmKSuYNTcYzXgUSZ4UagXV0CogcKlZLFA62W9nJAd/hSHFaSueIDjVPveoEP8JjRSgqkY91qXbLUih5FrcNV1Np/IQ75CiUXBX6ApaGp6k0lgTtHY0nW7/AgtD0NEqhs0VBc89Gq6xh8fO4T7g9bnRq2CdDpTj9dYxcxL1C99ZG5YSg8ZesO/SbGFkRukU61t5G5LCgra/4ZhWapr7Cy0pumrI24IxQeBjjArmwNvZ+AxrPaDdOviO02OZis3W2AAc02CFUYj0kX/P0Z/jECDVPt6Nd+/yU9e3zdRHa5/8FGLnMg5/e7DMAAAAASUVORK5CYII=";
var pattern = "data:image/jpeg;base64,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AABSZ3NNYm9vbAAAAAAAQ3JuQ2Jvb2wAAAAAAENudENib29sAAAAAABMYmxzYm9vbAAAAAAATmd0dmJvb2wAAAAAAEVtbERib29sAAAAAABJbnRyYm9vbAAAAAAAQmNrZ09iamMAAAABAAAAAAAAUkdCQwAAAAMAAAAAUmQgIGRvdWJAb+AAAAAAAAAAAABHcm4gZG91YkBv4AAAAAAAAAAAAEJsICBkb3ViQG/gAAAAAAAAAAAAQnJkVFVudEYjUmx0AAAAAAAAAAAAAAAAQmxkIFVudEYjUmx0AAAAAAAAAAAAAAAAUnNsdFVudEYjUHhsQFIAAAAAAAAAAAAKdmVjdG9yRGF0YWJvb2wBAAAAAFBnUHNlbnVtAAAAAFBnUHMAAAAAUGdQQwAAAABMZWZ0VW50RiNSbHQAAAAAAAAAAAAAAABUb3AgVW50RiNSbHQAAAAAAAAAAAAAAABTY2wgVW50RiNQcmNAWQAAAAAAAAAAABBjcm9wV2hlblByaW50aW5nYm9vbAAAAAAOY3JvcFJlY3RCb3R0b21sb25nAAAAAAAAAAxjcm9wUmVjdExlZnRsb25nAAAAAAAAAA1jcm9wUmVjdFJpZ2h0bG9uZwAAAAAAAAALY3JvcFJlY3RUb3Bsb25nAAAAAAA4QklNA+0AAAAAABAASAAAAAEAAgBIAAAAAQACOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD+AAAA4QklNA/IAAAAAAAoAAP///////wAAOEJJTQQNAAAAAAAEAAAAWjhCSU0EGQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAAAAAAAAEAOEJJTScQAAAAAAAKAAEAAAAAAAAAAjhCSU0D9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQQeAAAAAAAEAAAAADhCSU0EGgAAAAADQwAAAAYAAAAAAAAAAAAAAIAAAACAAAAABwBwAGEAdAB0AGUAcgBuAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAABAAAAABAAAAAAAAbnVsbAAAAAIAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3QxAAAABAAAAABUb3AgbG9uZwAAAAAAAAAATGVmdGxvbmcAAAAAAAAAAEJ0b21sb25nAAAAgAAAAABSZ2h0bG9uZwAAAIAAAAAGc2xpY2VzVmxMcwAAAAFPYmpjAAAAAQAAAAAABXNsaWNlAAAAEgAAAAdzbGljZUlEbG9uZwAAAAAAAAAHZ3JvdXBJRGxvbmcAAAAAAAAABm9yaWdpbmVudW0AAAAMRVNsaWNlT3JpZ2luAAAADWF1dG9HZW5lcmF0ZWQAAAAAVHlwZWVudW0AAAAKRVNsaWNlVHlwZQAAAABJbWcgAAAABmJvdW5kc09iamMAAAABAAAAAAAAUmN0MQAAAAQAAAAAVG9wIGxvbmcAAAAAAAAAAExlZnRsb25nAAAAAAAAAABCdG9tbG9uZwAAAIAAAAAAUmdodGxvbmcAAACAAAAAA3VybFRFWFQAAAABAAAAAAAAbnVsbFRFWFQAAAABAAAAAAAATXNnZVRFWFQAAAABAAAAAAAGYWx0VGFnVEVYVAAAAAEAAAAAAA5jZWxsVGV4dElzSFRNTGJvb2wBAAAACGNlbGxUZXh0VEVYVAAAAAEAAAAAAAlob3J6QWxpZ25lbnVtAAAAD0VTbGljZUhvcnpBbGlnbgAAAAdkZWZhdWx0AAAACXZlcnRBbGlnbmVudW0AAAAPRVNsaWNlVmVydEFsaWduAAAAB2RlZmF1bHQAAAALYmdDb2xvclR5cGVlbnVtAAAAEUVTbGljZUJHQ29sb3JUeXBlAAAAAE5vbmUAAAAJdG9wT3V0c2V0bG9uZwAAAAAAAAAKbGVmdE91dHNldGxvbmcAAAAAAAAADGJvdHRvbU91dHNldGxvbmcAAAAAAAAAC3JpZ2h0T3V0c2V0bG9uZwAAAAAAOEJJTQQoAAAAAAAMAAAAAj/wAAAAAAAAOEJJTQQUAAAAAAAEAAAADThCSU0EDAAAAAAD6gAAAAEAAACAAAAAgAAAAYAAAMAAAAADzgAYAAH/2P/tAAxBZG9iZV9DTQAB/+4ADkFkb2JlAGSAAAAAAf/bAIQADAgICAkIDAkJDBELCgsRFQ8MDA8VGBMTFRMTGBEMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAENCwsNDg0QDg4QFA4ODhQUDg4ODhQRDAwMDAwREQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM/8AAEQgAgACAAwEiAAIRAQMRAf/dAAQACP/EAT8AAAEFAQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5/cRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14/NGlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x//aAAwDAQACEQMRAD8AoJJJLtH0JSSSSSnsRwkkOEljPzcpJJJBTmJJJLzFuqTHhOmPCSnzE8lMnPJTL6Be1CkkkkUv/9Cgkkku0fQlJJJJKexHCSYcJ1ivzcpJJJJTmJJJLzFuqTHhOmPCSnzE8lMnPJTL6Be1Ukkkil//0fPkkkl6c01JxyEycchBD6cOE6YcJ18/PFKSSSSU6aSSS9OaSkx4TpHhJTxySSS2n6RUkkkkp//S8+SSSXpzTUnHITJxyEEPpw4TphwnXz88UpJJJJTppJJL05pKSPCSY8JKePSSSW0/SKkkkklP/9Ogkkku0fQlJJJJKewHCdIcJLFfm5SSSSSnMSSSXmLdUmPCdMeElPmJ5KZOeSmX0C9qpJJJFL//1KCSSS7R9CUkkkkp7EcJJhwnWK/NykkkklOYkkkvMW6pMeE6Y8JKfMTyUyc8lMvoF7VSSSSKX//V8+SSSXpzTUnHITJxyEEPpw4TphwnXz88UpJJJJTppJJL05pKTHhOkeElPHJJJLafpFSSSSSn/9bz5JJJenNNScchMnHIQQX04cJ0w4Tr5+eKUkkkkp00kkl6c0lJHhJI8IqeOSSSWy/SKkkkklP/2ThCSU0EIQAAAAAAUwAAAAEBAAAADwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAAABIAQQBkAG8AYgBlACAAUABoAG8AdABvAHMAaABvAHAAIABDAEMAAAABADhCSU0EBgAAAAAABwAHAQEAAQEA/+EO/2h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDAgNzkuMTYwNDUxLCAyMDE3LzA1LzA2LTAxOjA4OjIxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA1LTAxVDEyOjIyOjA5KzAzOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTA1LTAxVDEyOjQ0OjI1KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wNS0wMVQxMjo0NDoyNSswMzowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5NjU5MzU1MC1mNGVhLTAyNGUtOWM1YS0zMmNiYjNjMmQwZmYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzM0N2QzZi1iZTA0LTQxNTQtODBlMS00ZWVhNDVmMTM0MDQiIGRjOmZvcm1hdD0iaW1hZ2UvanBlZyIgcGhvdG9zaG9wOkxlZ2FjeUlQVENEaWdlc3Q9IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0iRGlzcGxheSBQMyI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDMzNDdkM2YtYmUwNC00MTU0LTgwZTEtNGVlYTQ1ZjEzNDA0IiBzdEV2dDp3aGVuPSIyMDIwLTA1LTAxVDEyOjIyOjA5KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc3MDM5N2M0LTI3YTItNDM4ZS05MzZiLTAwMzA2MTA2YjAwMSIgc3RFdnQ6d2hlbj0iMjAyMC0wNS0wMVQxMjoyMjowOSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHN0RXZ0OndoZW49IjIwMjAtMDUtMDFUMTI6NDQ6MjUrMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/Pv/iAjRJQ0NfUFJPRklMRQABAQAAAiRhcHBsBAAAAG1udHJSR0IgWFlaIAfhAAcABwANABYAIGFjc3BBUFBMAAAAAEFQUEwAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbMoalYIlfxBNOJkT1dHqFYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmRlc2MAAAD8AAAAZWNwcnQAAAFkAAAAI3d0cHQAAAGIAAAAFHJYWVoAAAGcAAAAFGdYWVoAAAGwAAAAFGJYWVoAAAHEAAAAFHJUUkMAAAHYAAAAIGNoYWQAAAH4AAAALGJUUkMAAAHYAAAAIGdUUkMAAAHYAAAAIGRlc2MAAAAAAAAAC0Rpc3BsYXkgUDMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxNwAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAg98A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";
var width = 512,
height = 512;
inits();
function inits(){
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
scene.add(camera);
var N = 8.0;
var points = [];
var colors = [];
for(var y = 16; y <= height - 16; y += N){
for(var x = 16; x <= width - 16; x += N){
var dx = -1.0 + x / width * 2;
var dy = -1.0 + y / height * 2;
points.push(new THREE.Vector2(dx, dy));
}
}
buffer = {
position: new Float32Array(points.length * 3),
color: new Float32Array(points.length * 3),
radius: new Float32Array(points.length * 1),
};
for(var i = 0; i < points.length; i++){
buffer.position[i * 3] = points[i].x;
buffer.position[i * 3 + 1] = points[i].y;
buffer.position[i * 3 + 2] = 0;
buffer.radius[i] = N * 0.5;
buffer.color[i * 3] = 1.0;
buffer.color[i * 3 + 1] = 0.0;
buffer.color[i * 3 + 2] = 1.0;
}
uniforms = {
blob: { value: new THREE.TextureLoader().load(blob) },
pattern: { value: new THREE.TextureLoader().load(pattern) }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
} );
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var cloud = new THREE.BufferGeometry();
cloud.addAttribute('position', new THREE.BufferAttribute(buffer.position, 3));
cloud.addAttribute('radius', new THREE.BufferAttribute(buffer.radius, 1));
cloud.addAttribute('color', new THREE.BufferAttribute(buffer.color, 3));
var points = new THREE.Points(cloud, material);
points.dynamic = true;
points.frustumCulled = false;
scene.add(points);
animate();
}
function animate() {
renderer.render( scene, camera );
requestAnimationFrame( animate );
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PixelWordMap Demo</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<meta name="author" content="Vladimir V. KUCHINOV">
<link rel="stylesheet" href="styles.css" charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.js"></script>
</head>
<body>
<script type='x-shader/x-vertex' id='vertexShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
attribute float radius;
attribute vec3 color;
varying vec3 vColor;
varying vec2 pos;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = radius;
pos = vec2(1.0 + position.x / 2.0, 1.0 + position.y / 2.0);
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type='x-shader/x-fragment' id='fragmentShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
varying vec3 vColor;
varying vec2 pos;
void main() {
vec4 color = texture2D(pattern, vec2(pos.x, pos.y));
gl_FragColor = color * texture2D(blob, gl_PointCoord);
if(gl_FragColor.a < 0.1) discard;
}
</script>
</body>
</html>
The vertex shader output varying vec2 pos; is never assigned a value, because the position is assigned to the local variable pos with the same name: vec2 pos = vec2(...);.
Furthermore you missed the braces, to prioritized the + operation:
pos = (position.xy + 1.0) / 2.0;
Choose a different number for N to get a different number of points:
var N = 32;
for(var y = N/2; y <= height; y += N){
for(var x = N/2; x <= width; x += N){
var dx = -1.0 + x / width * 2;
var dy = -1.0 + y / height * 2;
points.push(new THREE.Vector2(dx, dy));
}
}
Note, the point size is limited. See Working around gl_PointSize limitations in three.js / webGL.
var blob = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAMVWlDQ1BEaXNwbGF5AABIiZVXd1RTdxt+7khCQtiIgoywlyiiIENmmIKAbHARkgBhhHhJUHFbShWsW0RxVLQqYrXVCkgdiFpcxb3q+FCLo1KLA7fy/ZFAbf3O953vd8699z3P+7zPO+49OXkBvXUihaKA1AcK5UomISJEkJaeIeDcAwES2nCBi0hcrAiOj48BgL7n38/LKyAA4KKbSKEo+Nz/X4+BRFosBoh4AFmSYnEhQPwI0GViBaME2N4AbKcqFUqAPQGAMZOWngGwFQCMc9R2GQDjLLVdDcCYSUoQAuydgBZfJGJyAN0mAIIScY4S0L0GwF0ukckBPS0AAeJckQTQiwQwpLCwSALoKQE4ZX2ik/M3zax+TZEop99W9wIA0AqVFSsKRNP/z3H871NYoOrL4QCAn8tEJgAwBohr+UXRCQD4ANElz4qNA2AIEK9lEkBtk7xcVWSymk+ai4uFGQBMANJdIgqNBmAOkOHygtgYDZ6VLQuPAqAPkNNkyqgkTewCaXFYokZzHVOUENdnZzPCYE3sLhEDaPjHVPnJwRr9a7nSqD79F6W5SanqmileiSwlFoAuQJkU5ydGqzmUXWmuMLaPw6gSkgHYAZSvVB4RotanJmUz4QkaPlNY3NcvtSBXFhWrsdcqc5MiNTo7xaKwRACDAKpJKg9O7tORFqfF9PUikYaGqXunzknlyZp+qQ6FMiRBE/tMURCv4dM8aUFEAgAbgDYvLknUxNIBSiZJ847oWIUyPkldJ52VJxoTr66HnoYYCBEKAVQQIAtFyIOsvauxCwKNJxwiMMiBFG4apC8iFSIwkEOERJTiD8ghRXF/XAhEYCBFCeT40I+q727IhggMSiBFMfJxHwwKEY0CSKECAynk/dlS8BsYyD7LLkYRClAEBrL/gAVDiBgNourTFej1Mdlh7FB2JDuc7Uyb0QG0Hx1DB9BBdADtQXvTPn3V/sVn3WedZ91lXWZ1sK5Pls1n/tGPAGPRAZVmVlJkfdoz7UB70J50CO1PB9A+ENAmtBnc6JG0Nx1MB9J+tCftA6GmchU+1/5bD59MXcPjunNJ7kBuENfpn5G6Lrqe/SpSyP82IXWtWf1zFfZ7/plf+MmkJShC9D+Z1AJqL9VGHaFOUgeoRgiow1QTdYY6SDV+8hX9BgY5/dkSIIUc+SiA7LN8Ik1OBlIUu9e7P3J/r/YppdOUACAsUkxnZDm5SkGwQlEgFUTJxUOHCDzch/sAaekZAvXP1HMTEAAIk1N/YVNaAJ8KgMj5CxPZAvvvA0Yv/8JsnwH8pcDBc2IVU6LGaABggQc9GMMUlrCFE9zgAS/4IQhhGIM4JCEdkyBGLgrBYCpmYh7KUYmlWIW12IjN2I7vsAeNOIAj+BmncQ6XcQMd6MRjdOMl3hEEwSF0CCPClLAi7AlXwoPwJgKIMCKGSCDSiUwih5ATKmIm8QVRSSwn1hKbiDriB2I/cYQ4SZwnrhN3iEfEM+ItSZF80pi0IB3IYaQ3GUxGk0nkRDKHnEKWkmXkYrKarCV3kg3kEfI0eZnsIB+TPRQobcqEsqbcKG9KSMVRGVQ2xVCzqQqqiqqldlHNVBt1keqguqg3NJs2ogW0G+1HR9LJtJieQs+mF9Fr6e10A32MvkjfobvpjywdljnLleXLimKlsXJYU1nlrCrWVtY+1nHWZVYn6yWbzTZhO7JHsSPZ6ew89gz2IvZ69m52C/s8+x67h8PhmHJcOf6cOI6Io+SUc9ZwdnIOcy5wOjmvtbS1rLQ8tMK1MrTkWvO1qrR2aB3SuqD1QOsdV59rz/XlxnEl3OncJdwt3GbuWW4n9x3PgOfI8+cl8fJ483jVvF2847ybvOfa2to22j7a47Rl2nO1q7W/1z6hfUf7Dd+Q78IX8ifwVfzF/G38Fv51/nMdHR0HnSCdDB2lzmKdOp2jOrd1Xusa6Q7VjdKV6M7RrdFt0L2g+0SPq2evF6w3Sa9Ur0pvr95ZvS59rr6DvlBfpD9bv0Z/v/5V/R4DI4PhBnEGhQaLDHYYnDR4aMgxdDAMM5QYlhluNjxqeM+IMrI1EhqJjb4w2mJ03KjTmG3saBxlnGdcafydcbtx9wDDASMHpAyYNqBmwMEBHSaUiYNJlEmByRKTPSZXTN4OtBgYPFA6cOHAXQMvDHw1aPCgoEHSQRWDdg+6POitqcA0zDTfdJlpo+ktM9rMxWyc2VSzDWbHzboGGw/2GyweXDF4z+BfzUlzF/ME8xnmm83PmPdYWFpEWCgs1lgcteiyNLEMssyzXGl5yPKRlZFVgJXMaqXVYavfBQMEwYICQbXgmKDb2tw60lplvcm63fqdjaNNss18m902t2x5tt622bYrbVttu+2s7MbazbSrt/vVnmvvbZ9rv9q+zf6Vg6NDqsNXDo0ODx0HOUY5ljrWO9500nEKdJriVOt0yZnt7O2c77ze+ZwL6eLpkutS43LWlXT1cpW5rnc9P4Q1xGeIfEjtkKtufLdgtxK3erc7Q02GxgydP7Rx6JNhdsMyhi0b1jbso7une4H7Fvcbww2Hjxk+f3jz8GceLh5ijxqPSyN0RoSPmDOiacTTka4jpSM3jLzmaeQ51vMrz1bPD16jvBivXV6PRtmNyhy1btRVb2PveO9F3id8WD4hPnN8Dvi88fXyVfru8f3Tz80v32+H38PRjqOlo7eMvudv4y/y3+TfESAIyAz4JqAj0DpQFFgbeDfINkgStDXoQbBzcF7wzuAnIe4hTMi+kFdCX+EsYUsoFRoRWhHaHmYYlhy2Nux2uE14Tnh9eHeEZ8SMiJZIVmR05LLIq1EWUeKouqjuMaPGzBpzLJofnRi9NvpujEsME9M8lhw7ZuyKsTdj7WPlsY1xiIuKWxF3K94xfkr8T+PY4+LH1Yy7nzA8YWZCW6JR4uTEHYkvk0KSliTdSHZKViW3puilTEipS3mVGpq6PLUjbVjarLTT6WbpsvSmDE5GSsbWjJ7xYeNXje+c4DmhfMKViY4Tp008OclsUsGkg5P1Josm781kZaZm7sh8L4oT1Yp6sqKy1mV1i4Xi1eLHkiDJSskjqb90ufRBtn/28uyHOf45K3Ie5QbmVuV2yYSytbKneZF5G/Ne5cflb8vvLUgt2F2oVZhZuF9uKM+XHyuyLJpWdF7hqihXdEzxnbJqSjcTzWwtJoonFjcpjZUK5RmVk+pL1Z2SgJKaktdTU6bunWYwTT7tzHSX6QunPygNL/12Bj1DPKN1pvXMeTPvzAqetWk2MTtrdusc2zllczrnRszdPo83L3/eL/Pd5y+f/+KL1C+ayyzK5pbd+zLiy/py3XKm/OpXfl9tXEAvkC1oXzhi4ZqFHyskFacq3SurKt8vEi869fXwr6u/7l2cvbh9ideSDUvZS+VLrywLXLZ9ucHy0uX3Voxd0bBSsLJi5YtVk1edrBpZtXE1b7VqdUd1THXTGrs1S9e8X5u79nJNSM3udebrFq57tV6y/sKGoA27NlpsrNz49hvZN9c2RWxqqHWordrM3lyy+f6WlC1t33p/W7fVbGvl1g/b5Ns6tidsP1Y3qq5uh/mOJfVkvar+0c4JO899F/pd0y63XZt2m+yu/B7fq77//YfMH67sid7Tutd7764f7X9ct89oX0UD0TC9obsxt7GjKb3p/P4x+1ub/Zr3/TT0p20HrA/UHBxwcMkh3qGyQ72HSw/3tChauo7kHLnXOrn1xtG0o5eOjTvWfjz6+Imfw38+2hbcdviE/4kDJ31P7j/lfarxtNfphjOeZ/b94vnLvnav9oazo842nfM513x+9PlDFwIvHLkYevHnS1GXTl+OvXz+SvKVa1cnXO24Jrn28HrB9ae/lvz67sbcm6ybFbf0b1XdNr9d+y/nf+3u8Oo4eCf0zpm7iXdv3BPfe/xb8W/vO8vu69yvemD1oO6hx8MDj8Ifnft9/O+djxWP33WV/2Hwx7onTk9+/DPozzPdad2dT5mnvc8WPTd9vu3FyBetPfE9t18Wvnz3quK16evtb7zftL1Nffvg3dT3nPfVH5w/NH+M/nizt7C3VyFiRAAACgCZnQ082wbopANG5wDeePWeBwAg1LspoP4P8p9t9S4IAPACtgUByXOBmBZgQwtgPxfgtwDxAJKCQI4Y0X9pTnH2CA+1Fp8BWK97e59bAJxm4APT2/tufW/vhy0AdR1omaLeLwGArQ98owsAJ9unfrYo/hut3X80KW0+GQAAAAlwSFlzAAALEwAACxMBAJqcGAAABrRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQwIDc5LjE2MDQ1MSwgMjAxNy8wNS8wNi0wMTowODoyMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNC0yOVQyMjozODowNCswMzowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDQtMjlUMjI6Mzg6MDQrMDM6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6OGM0MWExZjUtMGE3NS1iNjRhLThlY2UtYTI0YjRlNjdlZmE3IiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6NGU0ODQwM2UtNTYzNS00MmM4LTkxYzQtZjZlODIzMzk0MDg5IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9IkRpc3BsYXkiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjRlNDg0MDNlLTU2MzUtNDJjOC05MWM0LWY2ZTgyMzM5NDA4OSIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0yMVQxNTozMzo0MSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MjliNmNjMy01Y2Y3LTQzOWUtYTA1YS1lOTNhN2FjYzg2OWEiIHN0RXZ0OndoZW49IjIwMTktMDEtMjFUMTU6MzM6NDErMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZWE1YjE0NGQtNjczOS00YmYyLTg0ZmYtNmFkMjlkYTZmNTlkIiBzdEV2dDp3aGVuPSIyMDIwLTA0LTI5VDIyOjM4OjA0KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x4suaAAAA5FJREFUeJzt20uLHFUYxvHf1KgkKk4wMCEIUcGoMSAGyeBK8RLwsjFZ6CfwAuJ1bbI0++gmfgNduDOB6Ci6iYwKLtSxTSAmoHEGRm3BZBidlIszHXvavlR3ddWZrp4/NFRXV9V53rdP3d7LRJqmSuBa3Im7sBO3YBu2rP2+jD/wMy6ihh/xd9HCrinw2DvwKB7E/diDGzLu+xfm8TU+xywWCtBoooAZ8CyewZP++4fzsowTeB/vDemYGJ4DbsRLeAG3D+OAXTiH43gbl3IfLU3TvJ9X0zRdSMvn17Wxc+nPMwMewDHsz/0v5ONLvIIvBtk5GXDQN3FafOMJGk4Lmvqm3xlwHT7AU4MMVgIf4hBWsu7QjwN24lPhXr6RqeFh4XmiJ1kdcCvmMD24rlJZxAzO99owiwOm8T2259dVKku4R3BGR3o5YBJnFH9vL4pz2I3VThv0ugucNLrGE7Sf7LZBNwccxoGhyonDAcGWtnQ6Bfbi26IURWKvcC1bRycH1ITX1ypxRhub2p0Cz7fbsALsxoutK1tnwCTqsr+3jxqXMIV/GitaZ8Brqms8XC/YeJXWGbCEm0sUFIPfND3UNc+Ap1XfeIKNBxtfmh3wvwtEhXmusdA4BabwOyZiKSqZK8JMqDdmwOPGx3jCzH+isUAIX48bjxAcMCm8O48bM5icSNN0D74Rwl3jxAr2JULGZtyMJ9h8d4JdsZVEZFdidOJ8RTCdCFnacWVbgq2xVURk66CZoapwJcHl2CoispwIlRnjSj3RI3FQcRYTXIitIiIXEqEWJ3M2tUKs4IdEqMaajywmBvOoJULebC6ymBjMYbXxHDAbU0kkZtkMiV0NidVxKp6e0jkl2LwuKnw8jpYovNtY2EyMtPz4VrlaonC0+Uu75OifQg6tilzGTbokR1fxepmKSuYNTcYzXgUSZ4UagXV0CogcKlZLFA62W9nJAd/hSHFaSueIDjVPveoEP8JjRSgqkY91qXbLUih5FrcNV1Np/IQ75CiUXBX6ApaGp6k0lgTtHY0nW7/AgtD0NEqhs0VBc89Gq6xh8fO4T7g9bnRq2CdDpTj9dYxcxL1C99ZG5YSg8ZesO/SbGFkRukU61t5G5LCgra/4ZhWapr7Cy0pumrI24IxQeBjjArmwNvZ+AxrPaDdOviO02OZis3W2AAc02CFUYj0kX/P0Z/jECDVPt6Nd+/yU9e3zdRHa5/8FGLnMg5/e7DMAAAAASUVORK5CYII=";
var pattern = "data:image/jpeg;base64,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AABSZ3NNYm9vbAAAAAAAQ3JuQ2Jvb2wAAAAAAENudENib29sAAAAAABMYmxzYm9vbAAAAAAATmd0dmJvb2wAAAAAAEVtbERib29sAAAAAABJbnRyYm9vbAAAAAAAQmNrZ09iamMAAAABAAAAAAAAUkdCQwAAAAMAAAAAUmQgIGRvdWJAb+AAAAAAAAAAAABHcm4gZG91YkBv4AAAAAAAAAAAAEJsICBkb3ViQG/gAAAAAAAAAAAAQnJkVFVudEYjUmx0AAAAAAAAAAAAAAAAQmxkIFVudEYjUmx0AAAAAAAAAAAAAAAAUnNsdFVudEYjUHhsQFIAAAAAAAAAAAAKdmVjdG9yRGF0YWJvb2wBAAAAAFBnUHNlbnVtAAAAAFBnUHMAAAAAUGdQQwAAAABMZWZ0VW50RiNSbHQAAAAAAAAAAAAAAABUb3AgVW50RiNSbHQAAAAAAAAAAAAAAABTY2wgVW50RiNQcmNAWQAAAAAAAAAAABBjcm9wV2hlblByaW50aW5nYm9vbAAAAAAOY3JvcFJlY3RCb3R0b21sb25nAAAAAAAAAAxjcm9wUmVjdExlZnRsb25nAAAAAAAAAA1jcm9wUmVjdFJpZ2h0bG9uZwAAAAAAAAALY3JvcFJlY3RUb3Bsb25nAAAAAAA4QklNA+0AAAAAABAASAAAAAEAAgBIAAAAAQACOEJJTQQmAAAAAAAOAAAAAAAAAAAAAD+AAAA4QklNA/IAAAAAAAoAAP///////wAAOEJJTQQNAAAAAAAEAAAAWjhCSU0EGQAAAAAABAAAAB44QklNA/MAAAAAAAkAAAAAAAAAAAEAOEJJTScQAAAAAAAKAAEAAAAAAAAAAjhCSU0D9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQQeAAAAAAAEAAAAADhC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aWZ5RGF0ZT0iMjAyMC0wNS0wMVQxMjo0NDoyNSswMzowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5NjU5MzU1MC1mNGVhLTAyNGUtOWM1YS0zMmNiYjNjMmQwZmYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzM0N2QzZi1iZTA0LTQxNTQtODBlMS00ZWVhNDVmMTM0MDQiIGRjOmZvcm1hdD0iaW1hZ2UvanBlZyIgcGhvdG9zaG9wOkxlZ2FjeUlQVENEaWdlc3Q9IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0iRGlzcGxheSBQMyI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDMzNDdkM2YtYmUwNC00MTU0LTgwZTEtNGVlYTQ1ZjEzNDA0IiBzdEV2dDp3aGVuPSIyMDIwLTA1LTAxVDEyOjIyOjA5KzAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc3MDM5N2M0LTI3YTItNDM4ZS05MzZiLTAwMzA2MTA2YjAwMSIgc3RFdnQ6d2hlbj0iMjAyMC0wNS0wMVQxMjoyMjowOSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMDE1YTk5Yy02MDFhLTQ4ZTYtYmZjYy1lZjFmZDQ1MDA3YTAiIHN0RXZ0OndoZW49IjIwMjAtMDUtMDFUMTI6NDQ6MjUrMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/Pv/iAjRJQ0NfUFJPRklMRQABAQAAAiRhcHBsBAAAAG1udHJSR0IgWFlaIAfhAAcABwANABYAIGFjc3BBUFBMAAAAAEFQUEwAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbMoalYIlfxBNOJkT1dHqFYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmRlc2MAAAD8AAAAZWNwcnQAAAFkAAAAI3d0cHQAAAGIAAAAFHJYWVoAAAGcAAAAFGdYWVoAAAGwAAAAFGJYWVoAAAHEAAAAFHJUUkMAAAHYAAAAIGNoYWQAAAH4AAAALGJUUkMAAAHYAAAAIGdUUkMAAAHYAAAAIGRlc2MAAAAAAAAAC0Rpc3BsYXkgUDMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAxNwAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAg98A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";
var width = 512,
height = 512;
inits();
function inits(){
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
scene.add(camera);
var points = [];
var colors = [];
var N = 32;
for(var y = N/2; y <= height; y += N){
for(var x = N/2; x <= width; x += N){
var dx = -1.0 + x / width * 2;
var dy = -1.0 + y / height * 2;
points.push(new THREE.Vector2(dx, dy));
}
}
buffer = {
position: new Float32Array(points.length * 3),
color: new Float32Array(points.length * 3),
radius: new Float32Array(points.length * 1),
};
for(var i = 0; i < points.length; i++){
buffer.position[i * 3] = points[i].x;
buffer.position[i * 3 + 1] = points[i].y;
buffer.position[i * 3 + 2] = 0;
buffer.radius[i] = N * 0.5;
buffer.color[i * 3] = 1.0;
buffer.color[i * 3 + 1] = 0.0;
buffer.color[i * 3 + 2] = 1.0;
}
uniforms = {
blob: { value: new THREE.TextureLoader().load(blob) },
pattern: { value: new THREE.TextureLoader().load(pattern) }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
} );
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var cloud = new THREE.BufferGeometry();
cloud.addAttribute('position', new THREE.BufferAttribute(buffer.position, 3));
cloud.addAttribute('radius', new THREE.BufferAttribute(buffer.radius, 1));
cloud.addAttribute('color', new THREE.BufferAttribute(buffer.color, 3));
var points = new THREE.Points(cloud, material);
points.dynamic = true;
points.frustumCulled = false;
scene.add(points);
animate();
}
function animate() {
renderer.render( scene, camera );
requestAnimationFrame( animate );
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script type='x-shader/x-vertex' id='vertexShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
attribute float radius;
attribute vec3 color;
varying vec3 vColor;
varying vec2 pos;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = radius;
pos = (position.xy + 1.0) / 2.0;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type='x-shader/x-fragment' id='fragmentShader'>
uniform sampler2D blob;
uniform sampler2D pattern;
varying vec3 vColor;
varying vec2 pos;
void main() {
vec4 color = texture2D(pattern, vec2(pos.x, pos.y));
gl_FragColor = color * texture2D(blob, gl_PointCoord);
if(gl_FragColor.a < 0.1) discard;
}
</script>

How can I set diferrent colors and size for point in WebGL?

I did this using webgl. I wonder if it is possible to set the dots with different transparency and different sizes? How can this be done?
'use strict';
(function() {
let vertices = [];
let gl = particle.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
resize();
function resize() {
particle.width = innerWidth;
particle.height = innerHeight;
let step = 10,
w = Math.floor(particle.width/step),
h = Math.floor(particle.height/step);
vertices = [];
for (var x=0; x<w*100; x++)
for (var y=0; y<12; y++)
vertices.push(1/w + x*6/w - 2, 1/h + y/h)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}
let pid = gl.createProgram();
shader(`
attribute vec2 v;
uniform float time;
varying vec3 c;
float rand(float n) {
return fract(sin(n) * 4358.5453123);
}
float noise(float p) {
float fl = floor(p);
float fc = fract(p);
return mix(rand(fl), rand(fl + 1.0), fc);
}
mat3 rotateX(float a) {
return mat3(vec3( 1.0, -1.0, 0.0),
vec3( -1.0, cos(a), -sin(a)),
vec3( 2.0, sin(a), cos(a)));
}
mat3 rotateY(float a){
return mat3(vec3( cos(a), 0.0, sin(a)),
vec3( 0.0, 1.0, 0.0),
vec3(-sin(a), 0.0, cos(a)));
}
mat3 rotateZ(float a){
return mat3(vec3( cos(a), -sin(a), 0.0),
vec3( sin(a), cos(a), 0.0),
vec3( 0.0, 0.0, 1.0));
}
void main(void) {
vec2 p = v;
p.y += sin(p.x*4.)*noise(time/1000.);
p.x += sin(time/14. + p.y);
vec3 pos = vec3(p.xy, 1.)*rotateX(p.x*4. + time);
pos.y += sin(pos.x*2.);
gl_Position = vec4(pos, 1.);
gl_PointSize = 3.7;
gl_Position.z = 0.0;
c.rgb=vec3(0.03, 0.54, 0.04);
}
`, gl.VERTEX_SHADER);
shader(`
precision highp float;
varying vec3 c;
void main(void) {
gl_FragColor = vec4(c, 1.);
}
`, gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);
let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);
let timeUniform = gl.getUniformLocation(pid, 'time');
requestAnimationFrame(draw);
addEventListener('resize', resize)
function draw(t) {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 0.5);
gl.uniform1f(timeUniform, t/1000);
gl.drawArrays(gl.POINTS, 0, vertices.length/2);
requestAnimationFrame(draw);
}
function shader(src, type) {
let sid = gl.createShader(type);
gl.shaderSource(sid, src);
gl.compileShader(sid);
var message = gl.getShaderInfoLog(sid);
gl.attachShader(pid, sid);
if (message.length > 0) {
console.log(src.split("\n")
.map((str, i) => (""+(1+i))
.padStart(4, "0")+": "+str)
.join("\n"));
throw message;
}
}
}());
body {
margin: 0;
}
canvas {
display: block;
}
#particle {
background-image:url(https://i.imgur.com/HiAlf85.jpg);
}
#canvas {
position: absolute;
left: 0;
top: 0;
}
<canvas id="particle"></canvas>
<canvas id="canvas"></canvas>
You pass in more attributes.
Right now you're passing in a position for each point.
attribute vec2 v;
Also pass in a color and a size
attribute vec2 v;
attribute vec4 color;
attribute float size;
Fill out buffers with the per point color and per point size.
As for transparency you need to enable blending gl.enable(gl.BLEND) and you need to set the blending function gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) then set gl_FragColor to a premultipiled alpha color.
'use strict';
(function() {
let positions = [];
let colors = [];
let sizes = [];
const gl = particle.getContext('webgl');
const positionBuffer = gl.createBuffer();
const sizeBuffer = gl.createBuffer();
const colorBuffer = gl.createBuffer();
resize();
function resize() {
particle.width = innerWidth;
particle.height = innerHeight;
let step = 10,
w = Math.floor(particle.width / step),
h = Math.floor(particle.height / step);
positions = [];
colors = [];
sizes = [];
for (var x = 0; x < w * 100; x++) {
for (var y = 0; y < 12; y++) {
positions.push(1 / w + x * 6 / w - 2, 1 / h + y / h);
const color = [Math.random(), y / 12, 0, y / 12];
// pre-multiply alpha (could do this in shader)
color[0] *= color[3];
color[1] *= color[3];
color[2] *= color[3];
colors.push(...color);
sizes.push(10 + y * 2);
}
}
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sizes), gl.STATIC_DRAW);
}
let pid = gl.createProgram();
shader(`
attribute vec2 position;
attribute vec4 color;
attribute float size;
uniform float time;
varying vec4 c;
float rand(float n) {
return fract(sin(n) * 4358.5453123);
}
float noise(float p) {
float fl = floor(p);
float fc = fract(p);
return mix(rand(fl), rand(fl + 1.0), fc);
}
mat3 rotateX(float a) {
return mat3(vec3( 1.0, -1.0, 0.0),
vec3( -1.0, cos(a), -sin(a)),
vec3( 2.0, sin(a), cos(a)));
}
mat3 rotateY(float a){
return mat3(vec3( cos(a), 0.0, sin(a)),
vec3( 0.0, 1.0, 0.0),
vec3(-sin(a), 0.0, cos(a)));
}
mat3 rotateZ(float a){
return mat3(vec3( cos(a), -sin(a), 0.0),
vec3( sin(a), cos(a), 0.0),
vec3( 0.0, 0.0, 1.0));
}
void main(void) {
vec2 p = position;
p.y += sin(p.x*4.)*noise(time/1000.);
p.x += sin(time/14. + p.y);
vec3 pos = vec3(p.xy, 1.)*rotateX(p.x*4. + time);
pos.y += sin(pos.x*2.);
gl_Position = vec4(pos, 1.);
gl_PointSize = size;
gl_Position.z = 0.0;
c=color;
}
`, gl.VERTEX_SHADER);
shader(`
precision highp float;
varying vec4 c;
void main(void) {
gl_FragColor = c;
}
`, gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);
// this code really belongs in your render loop.
{
let v = gl.getAttribLocation(pid, "position");
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);
let sizeLoc = gl.getAttribLocation(pid, "size");
gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
gl.vertexAttribPointer(sizeLoc, 1, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(sizeLoc);
let colorLoc = gl.getAttribLocation(pid, "color");
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(colorLoc);
}
let timeUniform = gl.getUniformLocation(pid, 'time');
requestAnimationFrame(draw);
addEventListener('resize', resize)
function draw(t) {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 0.5);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.uniform1f(timeUniform, t / 1000);
gl.drawArrays(gl.POINTS, 0, positions.length / 2);
requestAnimationFrame(draw);
}
function shader(src, type) {
let sid = gl.createShader(type);
gl.shaderSource(sid, src);
gl.compileShader(sid);
var message = gl.getShaderInfoLog(sid);
gl.attachShader(pid, sid);
if (message.length > 0) {
console.log(src.split("\n")
.map((str, i) => ("" + (1 + i))
.padStart(4, "0") + ": " + str)
.join("\n"));
throw message;
}
}
}());
body {
margin: 0;
}
canvas {
display: block;
}
#particle {
background-image: url(https://i.imgur.com/HiAlf85.jpg);
}
#canvas {
position: absolute;
left: 0;
top: 0;
}
<canvas id="particle"></canvas>
Of course you don't have to use attributes. You can compute colors and/or sizes via some formula or whatever.
Let me note this is a fairly basic WebGL question so you might want to read some articles on WebGL

Categories