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