Three.js - Using multiple textures in a single PointCloud - javascript

I'm trying to use multiple textures in a single PointCloud using a ShaderMaterial. I'm passing a texture array to the shader along with texture index attributes and selecting the appropriate texture to use in the fragment shader.
Relevant Setup Code:
var particleCount = 100;
var uniforms = {
textures: {
type: 'tv',
value: this.getTextures()
}
};
var attributes = {
texIndex: {
type: 'f',
value: []
},
color: {
type: 'c',
value: []
},
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
attributes: attributes,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
transparent: true
});
var geometry = new THREE.Geometry();
for (var i = 0; i < particleCount; i++) {
geometry.vertices.push(new THREE.Vector3(
(Math.random() - 0.5) * 50, (Math.random() - 0.5) * 50, (Math.random() - 0.5) * 50));
attributes.texIndex.value.push(Math.random() * 3 | 0);
attributes.color.value.push(new THREE.Color(0xffffff));
}
var particles = new THREE.PointCloud(geometry, material);
particles.sortParticles = true;
this.container.add(particles);
Vertex Shader:
attribute vec3 color;
attribute float texIndex;
varying vec3 vColor;
varying float vTexIndex;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
vColor = color;
vTexIndex = texIndex;
gl_PointSize = 50.0;
gl_Position = projectionMatrix * mvPosition;
}
Fragment Shader:
uniform sampler2D textures[3];
varying vec3 vColor;
varying float vTexIndex;
void main() {
vec4 startColor = vec4(vColor, 1.0);
vec4 finalColor;
if (vTexIndex == 0.0) {
finalColor = texture2D(textures[0], gl_PointCoord);
} else if (vTexIndex == 1.0) {
finalColor = texture2D(textures[1], gl_PointCoord);
} else if (vTexIndex == 2.0) {
finalColor = texture2D(textures[2], gl_PointCoord);
}
gl_FragColor = startColor * finalColor;
}
The problem is some points (ones using a texture index higher than 0) are flickering for reasons and can't figure out. Other attempts have also seemed to flicker between textures rather than opacity.
An example of this can be seen at http://jsfiddle.net/6qrubbk6/4/.
I've given up on this over multiple projects but I'd love to find a solution once and for all. Any help is greatly appreciated.
Edit:
Checking if vTexIndex is < n, instead of == n solves the issue.
if (vTexIndex < 0.5) {
finalColor = texture2D(textures[0], gl_PointCoord);
} else if (vTexIndex < 1.5) {
finalColor = texture2D(textures[1], gl_PointCoord);
} else if (vTexIndex < 2.5) {
finalColor = texture2D(textures[2], gl_PointCoord);
}
As seen here: http://jsfiddle.net/6qrubbk6/5/

Also you can cast vTexIndex to int.
int textureIndex = int(vTexIndex + 0.5);
if (textureIndex == 0) {
finalColor = texture2D(textures[0], gl_PointCoord);
} else if (textureIndex == 1) {
finalColor = texture2D(textures[1], gl_PointCoord);
} else if (textureIndex == 2) {
finalColor = texture2D(textures[2], gl_PointCoord);
}

Thanks for replying to your own question. You helped me get started on a similar feature I was working.
I thought this might be helpful to someone else so I'm replying here.
I've created a fiddle that does what you're doing, but dynamically. You can add as many textures to the textures array and they will be dynamically added to the nodes. This was tricky to do in glsl, and required some hacky javascript templating.
To do this, I just created 2 methods that return the vertex and fragment shader to the shader material:
Fragment Shader Method:
World.prototype.getFragmentShader = function(numTextures){
var fragShader = `uniform sampler2D textures[${numTextures}];
varying vec3 vColor;
varying float vTexIndex;
void main() {
vec4 startColor = vec4(vColor, 1.0);
vec4 finalColor;
`;
for(var i = 0; i < numTextures; i++){
if(i == 0){
fragShader += `if (vTexIndex < ${i}.5) {
finalColor = texture2D(textures[${i}], gl_PointCoord);
}
`
}
else{
fragShader += `else if (vTexIndex < ${i}.5) {
finalColor = texture2D(textures[${i}], gl_PointCoord);
}
`
}
}
fragShader += `gl_FragColor = startColor * finalColor;
}`;
console.log('frag shader: ', fragShader)
return fragShader;
}
Vertex Shader:
World.prototype.getVertexShader = function(){
let vertexShader = `attribute vec3 color;
attribute float texIndex;
varying vec3 vColor;
varying float vTexIndex;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
vColor = color;
vTexIndex = texIndex;
gl_PointSize = 50.0;
gl_Position = projectionMatrix * mvPosition;
}`;
return vertexShader;
}
You can see a live demo here: http://jsfiddle.net/jigglebilly/drmvz5co/

The new version of Three.js doesn't support attributes in ShaderMaterial. We'll have to delete attributes: attributes in new THREE.ShaderMaterial and use geometry.addAttribute instead. Here's the code to define texIndex:
var vIndex = new Float32Array( vertices.length );
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
vIndex[i] = Math.random()*getTextures().length;
}
geometry.addAttribute( 'texIndex', new THREE.BufferAttribute( vIndex, 1 ) );

Related

Can you make individual points in a THREE.js Points object transparent/invisible?

I have a Three.js Points object that contains data to display a bunch of points in the 3D space. I want to dynamically make some points invisible, but not sure how.
The material is a PointsMaterial. xyz data is stored in pointsObj.geometry.attributes.position.array and color data is stored in pointsObj.geometry.attributes.color.array, but I'm not sure if it's possible to alter things like alpha value or visibility of individual points (I can make all the points invisible, but this is different)
Does anyone know if this is possible?
Every point must have color.array, you can modify directly.
var colors = pointsObj.geometry.attributes.color.array;
for (var i = 0; i < colors.length; i += 4) {
if (shouldPointBeInvisible(i)) { //<-- not implemented
colors[i + 3] = 0; // Set alpha value to 0 to make the point invisible
}
}
pointsObj.geometry.attributes.color.needsUpdate = true;
-- Here with a shader material maybe:
var material = new THREE.ShaderMaterial({
uniforms: {
visibility: { value: 1.0 }
},
vertexShader: `
uniform float visibility;
varying vec3 vColor;
void main() {
vColor = color;
vColor.a *= visibility;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
}
`
});
pointsObj.material = material;
...
if (shouldPointBeInvisible(i)) {
pointsObj.material.uniforms.visibility.value = 0.0;
} else {
pointsObj.material.uniforms.visibility.value = 1.0;
}
-- And another solution with PointsMaterial:
var material = new THREE.PointsMaterial({
size: 1,
transparent: true,
opacity: 1.0,
uniforms: {
visibility: { value: 1.0 }
},
vertexShader: `
uniform float visibility;
varying vec3 vColor;
oid main() {
vColor = color;
vColor.a *= visibility;
gl_PointSize = size;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
, fragmentShader: varying vec3 vColor;
`
});
pointsObj.material = material;
...
if (shouldPointBeInvisible(i)) {
pointsObj.material.uniforms.visibility.value = 0.0;
} else {
pointsObj.material.uniforms.visibility.value = 1.0;
}

Trying to pass texture from framebuffer results in L_INVALID_OPERATION : glDrawArrays: Source and destination textures of the draw are the same

I'm new to vanilla WebGL and trying to utilize framebuffers for post processing/advanced shaders. When I run my code I get the warning:
GL_INVALID_OPERATION : glDrawArrays: Source and destination textures of the draw are the same.
Here's my code so far. If anyone could point me to the right direction how to correctly utilize framebuffers to pass textures to the next pass. It's wrapped in a vue.js component but that shouldn't matter.
<template lang='pug'>
canvas
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'webGl',
created ()
{
this.static = {
af: null,
gl: null,
fr: 0,
shaders:
{
vertex: `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
uniform vec2 u_size;
uniform int u_frame;
uniform sampler2D u_texture;
const int maxIter = 15;
vec2 getPos() {
vec2 pos = ( gl_FragCoord.xy / u_size.xy ) - vec2(0.5);
pos.x *= u_size.x / u_size.y;
return pos;
}
vec2 cmult(vec2 a, vec2 b){
return vec2(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
float length2(vec2 v){
return v.x*v.x+v.y*v.y;
}
vec2 map(vec2 pos){
return pos;
return vec2(pos.x * sqrt(1.-pos.y*pos.y*3.), pos.y * sqrt(1.-pos.x*pos.x*2.));
}
vec2 iterate(vec2 p, vec2 c){
vec2 p2 = cmult(p,p);
return p2 + c;
}
bool checkAbort(vec2 p, vec2 c){
return length2(p) > 400.;
}
float l2 = log(2.);
vec4 defaultColor ( void )
{
return vec4(0.35,0.35,0.35,1.0);
}
vec4 color(int iterations, vec2 p){
float col = .20 + (float(iterations) - log(log(length2(p)))/l2) / float(maxIter);
return defaultColor() * vec4(col);
}
void main( void ){
if (u_frame < 300)
{
vec2 c = map(getPos())*0.8 - vec2(0.5);
vec2 p = c + vec2(sin(-u_time), cos(u_time)) * 0.2;
float m;
for(int i = 0; i < maxIter ;i++) {
p = iterate(p,c);
if(checkAbort(p,c)){
gl_FragColor = color(i,p);
return;
}
}
gl_FragColor = defaultColor();
}
else
{
gl_FragColor = texture2D(u_texture, gl_FragCoord.xy / u_size.xy);
}
}`,
program: null,
attributes: {},
uniforms: {},
time: 0
}
}
},
mounted ()
{
this.setInitWebGlContext()
this.setInitShaderProgram()
this.setInitAttributes(['a_position'])
this.setInitUniforms(['u_size', 'u_time', 'u_frame', 'u_texture'])
this.setInitGeometryBuffer()
this.setRenderLoop()
},
beforeDestroy ()
{
window.cancelAnimationFrame(this.static.af)
},
computed:
{
...mapGetters([
'getCalcs'
])
},
methods:
{
setInitWebGlContext ()
{
this.static.gl = this.$el.getContext('webgl')
if (this.static.gl === null)
{
console.log('Unable to initialize WebGL. Your browser or machine may not support it.')
}
},
setInitShaderProgram ()
{
const gl = this.static.gl
this.static.shaders.program = gl.createProgram()
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, this.static.shaders.vertex)
gl.shaderSource(fragmentShader, this.static.shaders.fragment)
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
gl.attachShader(this.static.shaders.program, vertexShader)
gl.attachShader(this.static.shaders.program, fragmentShader)
gl.linkProgram(this.static.shaders.program)
gl.useProgram(this.static.shaders.program)
},
setInitAttributes (keys)
{
const gl = this.static.gl
const program = this.static.shaders.program
for (let i = 0; i < keys.length; i++)
{
this.static.shaders.attributes[keys[i]] = gl.getAttribLocation(program, keys[i])
}
},
setInitUniforms (keys)
{
const gl = this.static.gl
const program = this.static.shaders.program
for (let i = 0; i < keys.length; i++)
{
this.static.shaders.uniforms[keys[i]] = gl.getUniformLocation(program, keys[i])
}
},
setInitGeometryBuffer ()
{
const gl = this.static.gl
const buffer = gl.createBuffer()
gl.bindBuffer(this.static.gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW)
},
setCreateTexture ()
{
const gl = this.static.gl
const width = this.getCalcs.vw
const height = this.getCalcs.vh
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
return texture
},
setCreateFramebuffer ()
{
const gl = this.static.gl
const buffer = gl.createFramebuffer()
gl.bindFramebuffer(gl.FRAMEBUFFER, buffer)
const texture = this.setCreateTexture()
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)
return {
texture: texture,
buffer: buffer
}
},
setRenderLoop ()
{
this.static.af = window.requestAnimationFrame(this.setRenderLoop)
const gl = this.static.gl
const fb = this.static.fb
const width = this.getCalcs.vw
const height = this.getCalcs.vh
const attributes = this.static.shaders.attributes
const uniforms = this.static.shaders.uniforms
const mouse = this.static.shaders.mouse
const fr = this.static.fr
this.$el.width = width
this.$el.height = height
const bufferA = this.setCreateFramebuffer()
gl.viewport(0, 0, width, height)
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
gl.uniform1f(uniforms.u_time, window.performance.now() / 3000)
gl.uniform1i(uniforms.u_frame, fr)
gl.drawArrays(gl.TRIANGLES, 0, 6)
gl.bindTexture(gl.TEXTURE_2D, bufferA.texture)
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
gl.viewport(0, 0, width, height)
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
gl.uniform1f(uniforms.u_time, window.performance.now() / 3000)
gl.uniform1i(uniforms.u_frame, fr)
gl.uniform1i(uniforms.u_texture, 0)
gl.drawArrays(gl.TRIANGLES, 0, 6)
this.static.fr++
}
}
}
</script>
I'm now some steps further this code below is now working
<template lang='pug'>
canvas
</template>
<script>
import { mapGetters } from 'vuex'
import forEach from 'lodash/forEach'
export default {
name: 'webGl',
created ()
{
this.static = {
af: null,
gl: null,
fr: 0,
shaders:
{
noise:
{
vertex: `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_size;
vec2 hash( vec2 p ) {
p = vec2( dot(p,vec2(127.1,311.7)),
dot(p,vec2(269.5,183.3)));
return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}
float noise( vec2 p ) {
const float K1 = 0.366025404;
const float K2 = 0.211324865;
vec2 i = floor(p + (p.x + p.y) * K1);
vec2 a = p - i + (i.x + i.y) * K2;
vec2 o = step(a.yx, a.xy);
vec2 b = a - o + K2;
vec2 c = a - 1.0 + 2.0 * K2;
vec3 h = max(0.5 - vec3(dot(a,a), dot(b,b), dot(c,c)), 0.0);
vec3 n = h * h * h * h * vec3(dot(a, hash(i + 0.0)), dot(b, hash(i + o)), dot(c, hash(i + 1.0)));
return dot(n, vec3(70.0));
}
void main( void ) {
vec2 vUv = gl_FragCoord.xy / u_size.xy;
vec3 rnd = vec3(noise(16.0 * vUv + 1.1), noise(16.0 * vUv + 2.2), noise(16.0 * vUv + 3.3));
gl_FragColor = vec4(rnd, 1.0);
}`,
program: null,
attributes:
{
a_position: null
},
uniforms:
{
u_size: null
}
},
fluid:
{
vertex: `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_size;
uniform sampler2D u_image;
vec2 normz(vec2 x) {
return x == vec2(0.0, 0.0) ? vec2(0.0, 0.0) : normalize(x);
}
vec3 advect(vec2 ab, vec2 vUv, vec2 step, float sc) {
vec2 aUv = vUv - ab * sc * step;
const float _G0 = 0.25; // center weight
const float _G1 = 0.125; // edge-neighbors
const float _G2 = 0.0625; // vertex-neighbors
// 3x3 neighborhood coordinates
float step_x = step.x;
float step_y = step.y;
vec2 n = vec2(0.0, step_y);
vec2 ne = vec2(step_x, step_y);
vec2 e = vec2(step_x, 0.0);
vec2 se = vec2(step_x, -step_y);
vec2 s = vec2(0.0, -step_y);
vec2 sw = vec2(-step_x, -step_y);
vec2 w = vec2(-step_x, 0.0);
vec2 nw = vec2(-step_x, step_y);
vec3 uv = texture2D(u_image, fract(aUv)).xyz;
vec3 uv_n = texture2D(u_image, fract(aUv+n)).xyz;
vec3 uv_e = texture2D(u_image, fract(aUv+e)).xyz;
vec3 uv_s = texture2D(u_image, fract(aUv+s)).xyz;
vec3 uv_w = texture2D(u_image, fract(aUv+w)).xyz;
vec3 uv_nw = texture2D(u_image, fract(aUv+nw)).xyz;
vec3 uv_sw = texture2D(u_image, fract(aUv+sw)).xyz;
vec3 uv_ne = texture2D(u_image, fract(aUv+ne)).xyz;
vec3 uv_se = texture2D(u_image, fract(aUv+se)).xyz;
return _G0*uv + _G1*(uv_n + uv_e + uv_w + uv_s) + _G2*(uv_nw + uv_sw + uv_ne + uv_se);
}
void main( void ) {
const float _K0 = -20.0/6.0; // center weight
const float _K1 = 4.0/6.0; // edge-neighbors
const float _K2 = 1.0/6.0; // vertex-neighbors
const float cs = -0.6; // curl scale
const float ls = 0.05; // laplacian scale
const float ps = -0.8; // laplacian of divergence scale
const float ds = -0.05; // divergence scale
const float dp = -0.04; // divergence update scale
const float pl = 0.3; // divergence smoothing
const float ad = 6.0; // advection distance scale
const float pwr = 1.0; // power when deriving rotation angle from curl
const float amp = 1.0; // self-amplification
const float upd = 0.8; // update smoothing
const float sq2 = 0.6; // diagonal weight
vec2 vUv = gl_FragCoord.xy / u_size.xy;
vec2 texel = 1. / u_size.xy;
float step_x = texel.x;
float step_y = texel.y;
vec2 n = vec2(0.0, step_y);
vec2 ne = vec2(step_x, step_y);
vec2 e = vec2(step_x, 0.0);
vec2 se = vec2(step_x, -step_y);
vec2 s = vec2(0.0, -step_y);
vec2 sw = vec2(-step_x, -step_y);
vec2 w = vec2(-step_x, 0.0);
vec2 nw = vec2(-step_x, step_y);
vec3 uv = texture2D(u_image, fract(vUv)).xyz;
vec3 uv_n = texture2D(u_image, fract(vUv+n)).xyz;
vec3 uv_e = texture2D(u_image, fract(vUv+e)).xyz;
vec3 uv_s = texture2D(u_image, fract(vUv+s)).xyz;
vec3 uv_w = texture2D(u_image, fract(vUv+w)).xyz;
vec3 uv_nw = texture2D(u_image, fract(vUv+nw)).xyz;
vec3 uv_sw = texture2D(u_image, fract(vUv+sw)).xyz;
vec3 uv_ne = texture2D(u_image, fract(vUv+ne)).xyz;
vec3 uv_se = texture2D(u_image, fract(vUv+se)).xyz;
vec3 lapl = _K0*uv + _K1*(uv_n + uv_e + uv_w + uv_s) + _K2*(uv_nw + uv_sw + uv_ne + uv_se);
float sp = ps * lapl.z;
float curl = uv_n.x - uv_s.x - uv_e.y + uv_w.y + sq2 * (uv_nw.x + uv_nw.y + uv_ne.x - uv_ne.y + uv_sw.y - uv_sw.x - uv_se.y - uv_se.x);
float sc = cs * sign(curl) * pow(abs(curl), pwr);
float div = uv_s.y - uv_n.y - uv_e.x + uv_w.x + sq2 * (uv_nw.x - uv_nw.y - uv_ne.x - uv_ne.y + uv_sw.x + uv_sw.y + uv_se.y - uv_se.x);
float sd = uv.z + dp * div + pl * lapl.z;
vec2 norm = normz(uv.xy);
vec3 ab = advect(vec2(uv.x, uv.y), vUv, texel, ad);
float ta = amp * ab.x + ls * lapl.x + norm.x * sp + uv.x * ds * sd;
float tb = amp * ab.y + ls * lapl.y + norm.y * sp + uv.y * ds * sd;
float a = ta * cos(sc) - tb * sin(sc);
float b = ta * sin(sc) + tb * cos(sc);
vec3 abd = upd * uv + (1.0 - upd) * vec3(a,b,sd);
abd.z = clamp(abd.z, -1.0, 1.0);
abd.xy = clamp(length(abd.xy) > 1.0 ? normz(abd.xy) : abd.xy, -1.0, 1.0);
gl_FragColor = vec4(abd, 0.0);
}`,
program: null,
attributes:
{
a_position: null
},
uniforms:
{
u_size: null
}
},
colorize:
{
vertex: `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_size;
uniform sampler2D u_image;
void main( void ) {
vec2 texel = 1. / u_size.xy;
vec2 uv = gl_FragCoord.xy / u_size.xy;
vec3 c = texture2D(u_image, uv).xyz;
vec3 norm = normalize(c);
vec3 div = vec3(0.1) * norm.z;
vec3 rbcol = 0.5 + 0.6 * cross(norm.xyz, vec3(0.5, -0.4, 0.5));
gl_FragColor = vec4(rbcol + div, 1.0);
}`,
program: null,
attributes:
{
a_position: null
},
uniforms: {
u_size: null
}
}
},
textures:
{
default: null
}
}
},
mounted ()
{
this.setInitWebGlContext()
this.setInitGeometryBuffer()
this.setInitShaderPrograms()
this.setRenderLoop()
},
beforeDestroy ()
{
window.cancelAnimationFrame(this.static.af)
},
computed:
{
...mapGetters([
'getCalcs'
])
},
methods:
{
setInitWebGlContext ()
{
this.static.gl = this.$el.getContext('webgl')
if (this.static.gl === null)
{
console.log('Unable to initialize WebGL. Your browser or machine may not support it.')
}
},
setInitShaderPrograms ()
{
const gl = this.static.gl
forEach(this.static.shaders, shader =>
{
shader.program = gl.createProgram()
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, shader.vertex)
gl.shaderSource(fragmentShader, shader.fragment)
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
gl.attachShader(shader.program, vertexShader)
gl.attachShader(shader.program, fragmentShader)
gl.linkProgram(shader.program)
})
},
setInitGeometryBuffer ()
{
const gl = this.static.gl
const buffer = gl.createBuffer()
gl.bindBuffer(this.static.gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW)
},
setDraw (width, height)
{
const gl = this.static.gl
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.viewport(0, 0, width, height)
gl.drawArrays(gl.TRIANGLES, 0, 6)
},
setProgram (shader)
{
const gl = this.static.gl
const program = this.static.shaders[shader].program
const attributes = this.static.shaders[shader].attributes
const uniforms = this.static.shaders[shader].uniforms
gl.useProgram(program)
forEach(attributes, (attribute, key) =>
{
attributes[key] = gl.getAttribLocation(program, key)
})
forEach(uniforms, (uniform, key) =>
{
uniforms[key] = gl.getUniformLocation(program, key)
})
},
setFrameBuffer (width, height)
{
const gl = this.static.gl
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
const framebuffer = gl.createFramebuffer()
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)
return {
frameBuffer: framebuffer,
texture: texture
}
},
setNoise (width, height)
{
const gl = this.static.gl
const attributes = this.static.shaders.noise.attributes
const uniforms = this.static.shaders.noise.uniforms
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
},
setFluid (width, height)
{
const gl = this.static.gl
const attributes = this.static.shaders.fluid.attributes
const uniforms = this.static.shaders.fluid.uniforms
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
},
setColorize (width, height)
{
const gl = this.static.gl
const attributes = this.static.shaders.colorize.attributes
const uniforms = this.static.shaders.colorize.uniforms
gl.enableVertexAttribArray(attributes.a_position)
gl.vertexAttribPointer(attributes.a_position, 2, gl.FLOAT, false, 0, 0)
gl.uniform2f(uniforms.u_size, width, height)
},
setRenderLoop ()
{
this.static.af = window.requestAnimationFrame(this.setRenderLoop)
const gl = this.static.gl
const width = this.getCalcs.vw
const height = this.getCalcs.vh
this.$el.width = width
this.$el.height = height
if (!this.static.fr)
{
const noiseBuffer = this.setFrameBuffer(width, height)
this.setProgram('noise')
this.setNoise(width, height)
this.setDraw(width, height)
this.static.textures.default = noiseBuffer.texture
}
const fluidBuffer = this.setFrameBuffer(width, height)
gl.bindTexture(gl.TEXTURE_2D, this.static.textures.default)
this.setProgram('fluid')
this.setFluid(width, height)
this.setDraw(width, height)
this.static.textures.default = fluidBuffer.texture
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
this.setProgram('colorize')
this.setColorize(width, height)
this.setDraw(width, height)
this.static.fr++
}
}
}
</script>
However i'm trying to adopt a shader from shadertoy https://www.shadertoy.com/view/XddSRX and if i rum my code it's behaving very differently.
The issue is exactly as stated in the error.
Source and destination textures of the draw are the same.
Looking at your code there is one shader, it references a texture, there is one texture, it's attached to the framebuffer AND it's bound to texture unit 0 the default. So, when you draw it's being used as both an input (u_texture) and as the output (the current framebuffer). That's not allowed.
The simple solution is you need another texture. Bind that texture when drawing to the framebuffer.
The better solution is you need 2 different shader programs. One for when drawing to the framebuffer that uses no texture as input and another for drawing to the canvas . As it is you have one shader that branches on u_frame. Remove that branch and separate things into 2 shader programs. The one that computes colors then u_frame < 300 and the one that use a texture. Use the computing one to draw to the framebuffer and the texture one to draw the framebuffer's texture to the canvas.
A few links that may or may not be helpful: drawing multiple things, render targets, image processing.

How to pass and use a lookup table in a shader with three.js

I have written a simple three.js of using a height map. This is the relevant code that creates the shader material:
function loadHeightMap() {
// fake a lookup table
var lut = [];
for ( var n=0; n<256; n++ ) {
lut.push(new THREE.Vector3(0.5, 0.4, 0.3));
}
var loader = new THREE.TextureLoader();
var zScale = 10;
var mapLoc = "https://s22.postimg.org/8n93ehmep/Terrain128.png";
loader.load(mapLoc, function ( texture ) {
// use "this." to create global object
this.customUniforms = {
zTexture: { type: "t", value: texture },
zScale: { type: "f", value: zScale },
zLut: { type: "v3v", value: lut }
};
var customMaterial = new THREE.ShaderMaterial({
uniforms: customUniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.DoubleSide
});
var planeGeo = new THREE.PlaneGeometry( 20, 20, 129, 129 );
var plane = new THREE.Mesh( planeGeo, customMaterial );
plane.rotation.x = -Math.PI / 2;
plane.position.y = 0;
scene.add(plane);
});
}
And here are the shaders:
<script id="vertexShader" type="x-shader/x-vertex">
uniform sampler2D zTexture;
uniform float zScale;
uniform vec3 zLut[ 256 ];
varying float vAmount;
void main() {
vec4 heightData = texture2D( zTexture, uv );
vAmount = heightData.r;
// move the position along the normal
vec3 newPosition = position + normal * zScale * vAmount;
gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-vertex">
uniform vec3 zLut[ 256 ];
varying float vAmount;
void main() {
int index = int(vAmount) * 255;
vec3 vColor = vec3(vAmount, vAmount, vAmount);
//gl_FragColor = vec4(zLut[index], 1.0);
gl_FragColor = vec4(vColor, 1.0);
}
The shaders and the height map part works fine. But I want to pass the lookup table (zLut). The above code works fine if I don't try to use the lookup table. A working example is here. I created a fiddle as well here but it fails because of CORS issues.
Any suggestions are welcome.
OK, solved this (mostly). The trick was to fetch the lookup color in the vertex shader, where one CAN index into an array with a non-const value. The pass the resulting color to the fragmentShader as a varying. So the two shaders end up being:
<script id="vertexShader" type="x-shader/x-vertex">
uniform sampler2D vTexture;
uniform float vScale;
uniform vec3 vLut[ 256 ];
varying vec3 vColor;
void main() {
vec4 heightData = texture2D( vTexture, uv );
// assuming map is grayscale it doesn't matter if you use r, g, or b.
float vAmount = heightData.r;
// fetch the color from the lookup table so it gets passed to the fragshader
int index = int(heightData.r * 255.0);
vColor = vLut[index];
// move the position along the normal
vec3 newPosition = position + normal * vScale * vAmount;
gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-vertex">
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
}
</script>
The remaining problem I have is that when rendered the colors are all flat. I tried forcing an update on the vertices in the animate function but didn't work. Still researching but the question here is solved (AFAIK).
You can see the result here

Error when adding a new object into existing javascript code

I am trying to resolve an issue of the cannot read property of undefined error when I try to add a new object I created into my javascript file.
Previously, I was able to successfully get 3 objects (a cow, plane, and teapot) in a scene on html and interact with them. When I went to replace the plane with my own object (in my case it is called willis), I received the error.
The error spicifically states:
prog4.js:131 Uncaught TypeError: Cannot read property 'positions' of undefined(…)
init
# prog4.js:131
onload
# prog4.html:12
I have changed nothing about the code, other than renaming 'plane' to 'willis' and making sure the files were in the correct locations.
Any help very much appreciated! My goal is to get 3 different objects in the scene from the original 3 and I was hoping to do it one at a time to see the progress.
<html>
<head>
<script type="text/javascript" src="webgl-utils.js"></script>
<script type="text/javascript" src="webgl-debug.js"></script>
<script type="text/javascript" src="cuon-utils.js"></script>
<script type="text/javascript" src="cuon-matrix.js"></script>
<script type="text/javascript" src="teapot.js"></script>
<script type="text/javascript" src="willis.js"></script>
<script type="text/javascript" src="cow.js"></script>
<script type="text/javascript" src="prog4.js"></script>
</head>
<body onload="init()">
<script id="vertexShader" type="x-shader/x-vertex">
precision mediump float;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform vec4 lightPosition;
attribute vec4 vertexPosition;
attribute vec3 vertexNormal;
varying vec3 fragmentNormal;
varying vec3 fragmentLight;
varying vec3 fragmentView;
varying vec4 fragmentPosition;
void main() {
mat4 modelViewMatrix = viewMatrix * modelMatrix;
vec4 p = vec4 (modelViewMatrix * vertexPosition);
vec4 q = vec4 (viewMatrix * lightPosition);
fragmentNormal = normalize(mat3(modelViewMatrix) * vertexNormal);
fragmentLight = normalize(vec3(q - p));
fragmentView = normalize(vec3(-p));
fragmentPosition = vertexPosition;
gl_Position = projectionMatrix * modelViewMatrix * vertexPosition;
}
</script>
<script id="lightingFragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fragmentNormal;
varying vec3 fragmentLight;
varying vec3 fragmentView;
uniform vec3 modelColor;
uniform vec3 lightColor;
void main() {
vec3 n = normalize(fragmentNormal);
vec3 l = normalize(fragmentLight);
vec3 v = normalize(fragmentView);
vec3 h = normalize(l+v);
float d = max(dot(l,n),0.0);
float s = pow(max(dot(h,n),0.0), 10.0);
vec3 fragmentColor = modelColor * lightColor * d + lightColor * s;
gl_FragColor = vec4(fragmentColor, 1.0);
}
</script>
<script id="rainbowFragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fragmentNormal;
varying vec3 fragmentLight;
varying vec3 fragmentView;
varying vec4 fragmentPosition;
uniform vec3 lightColor;
void main() {
vec3 modelColor;
if(fragmentPosition.y > 0.5) {
modelColor = vec3(1.0,0.0,0.0);
}
else if (fragmentPosition.y > 0.3) {
modelColor = vec3(0.0,0.0,1.0);
}
else if (fragmentPosition.y > 0.1) {
modelColor = vec3(0.0,1.0,0.1);
}
else {
modelColor = vec3(0.0,1.0,1.0);
}
vec3 fragmentColor = modelColor;
gl_FragColor = vec4(fragmentColor, 1.0);
}
</script>
<script id="goochFragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec3 fragmentNormal;
varying vec3 fragmentLight;
varying vec3 fragmentView;
uniform vec3 modelColor;
uniform vec3 lightColor;
void main() {
vec3 n = normalize(fragmentNormal);
vec3 l = normalize(fragmentLight);
vec3 v = normalize(fragmentView);
vec3 h = normalize(l+v);
float kg = max(dot(n,v), 0.0);
vec3 fragmentColor = mix(vec3(0.0,0.0,1.0), vec3(1.0,1.0,0.0), kg);
gl_FragColor = vec4(fragmentColor, 1.0);
}
</script>
<canvas id="webgl" width="500px" height="500px" >
This content requires WebGL.
</canvas>
</body>
var canvas
var gl
var willis
var teapot
var cow
// Interaction
var modelRotationX =0;
var modelRotationY =0;
var dragging = false;
var lastClientX = 0;
var lastClientY = 0;
// Mouse commands
function onmousedown(event) {
dragging = true;
lastClientX = event.clientX;
lastClientY = event.clientY;
}
function onmouseup(event) {
dragging = false;
}
function onmousemove(event) {
if (dragging) {
dX = event.clientX - lastClientX;
dY = event.clientY - lastClientY;
modelRotationY = modelRotationY + dX;
modelRotationX = modelRotationX + dY;
if (modelRotationX > 90.0) {
modelRotationX = 90.0;
}
if (modelRotationX < -90.0) {
modelRotationX = -90.0;
}
requestAnimationFrame(draw);
}
lastClientX = event.clientX;
lastClientY = event.clientY;
}
// FLatten
function flatten(a) {
return a.reduce(function (b, v) { b.push.apply(b, v); return b }, [])
}
// Create shaders for objects
function Shader (vertexId, fragmentId) {
this.program = createProgram(gl, document.getElementById(vertexId).text,
document.getElementById(fragmentId).text);
this.projectionMatrixLocation = gl.getUniformLocation(this.program, "projectionMatrix");
this.viewMatrixLocation = gl.getUniformLocation(this.program, "viewMatrix");
this.modelMatrixLocation = gl.getUniformLocation(this.program, "modelMatrix");
this.lightPositionLocation = gl.getUniformLocation(this.program, "lightPosition");
this.modelColorLocation = gl.getUniformLocation(this.program, "modelColor");
this.lightColorLocation = gl.getUniformLocation(this.program, "lightColor");
this.vertexPositionLocation = gl.getAttribLocation(this.program, 'vertexPosition');
this.vertexNormalLocation = gl.getAttribLocation(this.program, 'vertexNormal');
gl.enableVertexAttribArray(this.vertexPositionLocation);
gl.enableVertexAttribArray(this.vertexNormalLocation);
}
// Execution for shaders
Shader.prototype.use = function(projectionMatrix, viewMatrix, modelMatrix) {
gl.useProgram(this.program);
gl.uniformMatrix4fv(this.projectionMatrixLocation, false, projectionMatrix.elements);
gl.uniformMatrix4fv(this.viewMatrixLocation, false, viewMatrix.elements);
gl.uniformMatrix4fv(this.modelMatrixLocation, false, modelMatrix.elements);
gl.uniform4f(this.lightPositionLocation,0,8,8,1);
gl.uniform3f(this.modelColorLocation,1,1,1);
gl.uniform3f(this.lightColorLocation,0.6,0.4,0.2);
}
// Create models
function Model (positions, normals, triangles) {
this.positionArray = new Float32Array(flatten(positions));
this.normalArray = new Float32Array(flatten(normals));
this.triangleArray = new Uint16Array(flatten(triangles));
this.normalBuffer = gl.createBuffer();
this.positionBuffer = gl.createBuffer();
this.triangleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.normalArray, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this.positionArray, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.triangleBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.triangleArray, gl.STATIC_DRAW);
}
// Draw shaders on objects
Model.prototype.draw = function (shader_entry) {
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);
gl.vertexAttribPointer(shader_entry.vertexNormalLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
gl.vertexAttribPointer(shader_entry.vertexPositionLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.triangleBuffer);
gl.drawElements(gl.TRIANGLES, this.triangleArray.length, gl.UNSIGNED_SHORT, 0);
}
// Initialization
function init() {
// Initialize the GL context
canvas = document.getElementById('webgl');
gl = getWebGLContext(canvas, false);
lightingShader = new Shader('vertexShader', 'lightingFragmentShader');
rainbowShader = new Shader('vertexShader', 'rainbowFragmentShader');
goochShader = new Shader('vertexShader', 'goochFragmentShader');
teapotModel = new Model(teapot.positions, teapot.normals, teapot.triangles);
willisModel = new Model(willis.positions, willis.normals, willis.triangles);
cowModel = new Model(cow.positions, cow.normals, cow.triangles);
// Make mouse functions "properties of the canvas entity."
canvas.onmousedown = onmousedown;
canvas.onmouseup = onmouseup;
canvas.onmousemove = onmousemove;
// Depth test
gl.enable(gl.DEPTH_TEST);
// Request animation frame
requestAnimationFrame(draw);
}
// Draw
function draw() {
// Create colored background
gl.clearColor(0.2, 0.1, 0.0, 0.3);
gl.clear(gl.COLOR_BUFFER_BIT);
// Declaration of variables
var modelMatrix = new Matrix4();
var viewMatrix = new Matrix4();
var projectionMatrix = new Matrix4();
// Cow and rotation
var modelMatrix = new Matrix4();
modelMatrix.rotate(modelRotationX,1,0,0);
modelMatrix.rotate(modelRotationY,0,1,0);
viewMatrix.translate(0,0,-5)
projectionMatrix.perspective(60,1,1,10);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
// Cow shader
lightingShader.use(projectionMatrix, viewMatrix, modelMatrix);
cowModel.draw(lightingShader);
// willis
var modelMatrix = new Matrix4();
modelMatrix.rotate(modelRotationX,1,0,0);
modelMatrix.rotate(modelRotationY,0,1,0);
viewMatrix.translate(2,0,-1)
// willis shader
goochShader.use(projectionMatrix, viewMatrix, modelMatrix);
willisModel.draw(goochShader);
//Teapot
var modelMatrix = new Matrix4();
modelMatrix.rotate(modelRotationX,1,0,0);
modelMatrix.rotate(modelRotationY,0,1,0);
viewMatrix.translate(-5,0,-2)
// Teapot shader
rainbowShader.use(projectionMatrix, viewMatrix, modelMatrix);
teapotModel.draw(rainbowShader);
}

three.js, make dashed line through shader

I'm trying to make a shader for my dashed line; but only I can get is white line from origin to .. +x direction(maybe)
If I render this curve (Ellipse.curve), they do just fine. And with shader don't..
..of course, I don't know why.. please help me;;
<script type="x-shader/x-vertex" id="vs-orbit">
uniform float time;
attribute float sovereign;
varying vec3 vColor;
vec3 setColorBySovereign() {
vec3 color;
color.r = 0.5 - ( 0.5 * sovereign );
color.g = 0.25 + ( 0.25 * sovereign );
color.b = 0.5 + ( 0.25 * sovereign );
return _color;
};
void main() {
vColor = setColorBySovereign();
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
};
</script>
<script type="x-shader/x-fragment" id="fs-orbit">
attribute vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
};
</script>
// ... Below is where use shader above;
Ellipse.prototype.createOrbit = function( soveregin ) {
var shape, mater;
var sov = soveregin || 0.0;
shape = new THREE.Geometry();
var v2;
for(var i=0; i<721; i++) {
v2 = this.curve.getPoint(i);
shape.vertices.push(new THREE.Vector3(v2.x, v2.y, 0));
};
shape.computeLineDistances();
mater = new THREE.ShaderMaterial({
attribute: { soveregin: sov },
vertexShader: document.getElementById('vs-orbit'),
fragmentShader: document.getElementById('fs-orbit')
});
this.orbit = new THREE.Line( shape, mater, THREE.LineStrip );
return;
}
I solve this problem; after main(){} I shouldn't use ';' and other syntax errors too.
in fragment shader, I have to declare vColor with operator 'varying', just like vertex shader. In fragment shader; from GLSL Reference; varying is equal to in operator.

Categories