I know how to draw models, but I don't know how to draw a fullscreen background image.
I think I should draw it at the zfar of the camera. But I have no idea how to calculate its size...
In sketchfab, it seems the background image can implement the css propertybackground-size: cover. And I notice sketchfab do not implement it by css.
I really want to know how to implement this!
How can I draw a fullscreen background image in WebGL like like style background-size: cover?
Create a simple shader which draws a texture, without any transformations:
Vertex shader:
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
vertPos = inPos;
gl_Position = vec4( inPos, 0.0, 1.0 );
}
Fragment shader:
precision mediump float;
varying vec2 vertPos;
uniform sampler2D u_texture;
void main()
{
vec2 texCoord = vec2( vertPos.s, -vertPos.t ) * 0.5 + 0.5;
vec3 texColor = texture2D( u_texture, texCoord.st ).rgb;
gl_FragColor = vec4( texColor.rgb, 1.0 );
}
Create a an array buffer, with the 4 corner points of the XY-plane, of the Normalized device space, from (-1, -1) to (1, 1).
bufRect = gl.createBuffer()
gl.bindBuffer( gl.ARRAY_BUFFER, bufRect );
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [ -1, -1, 1, -1, 1, 1, -1, 1 ] ), gl.STATIC_DRAW );
Draw the rectangle to the entire canvas, by using the shader. Since you draw on every fragment, you do not need to clear the color plane. But you have to disable the depth test.
gl.disable( gl.DEPTH_TEST );
var texUnit = 1;
gl.activeTexture( gl.TEXTURE0 + texUnit );
gl.bindTexture( gl.TEXTURE_2D, textureObj );
var tex_loc = gl.getUniformLocation( progBG, "u_texture" );
gl.useProgram( progBG );
gl.uniform1i( tex_loc, texUnit );
var v_attr_inx = gl.getAttribLocation( progBG, "inPos" );
gl.bindBuffer( gl.ARRAY_BUFFER, bufRect );
gl.vertexAttribPointer( v_attr_inx, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( v_attr_inx );
gl.drawArrays( gl.TRIANGLE_FAN, 0, 4 );
gl.disableVertexAttribArray( v_attr_inx );
Enable the depth test and clear the depth buffer, before you draw the geometry of the scene:
gl.clear( gl.DEPTH_BUFFER_BIT );
gl.enable( gl.DEPTH_TEST );
.....
Note, when you load the texture, then you have to ensure that the width and the height of the texture is a power of 2.
var Texture = {};
Texture.HandleLoadedTexture2D = function( image, texture, flipY ) {
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture( gl.TEXTURE_2D, texture );
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
if ( flipY != undefined && flipY == true )
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
gl.bindTexture( gl.TEXTURE_2D, null );
return texture;
}
Texture.LoadTexture2D = function( name ) {
var texture = gl.createTexture();
texture.image = new Image(64,64);
texture.image.setAttribute('crossorigin', 'anonymous');
texture.image.onload = function () {
var canvas = document.createElement( 'canvas' );
canvas.width = 512;
canvas.height = 256;
var context = canvas.getContext( '2d' );
context.drawImage( texture.image, 0, 0, canvas.width, canvas.height );
Texture.HandleLoadedTexture2D( canvas, texture, true )
}
texture.image.src = name;
return texture;
}
textureObj = Texture.LoadTexture2D( "my_background_texture" );
See the code snippet:
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, 5, 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 = "";
var node = shaderScript.firstChild;
while (node) {
if (node.nodeType == 3) source += node.textContent;
node = node.nextSibling;
}
}
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 : 0;
}
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 : 0;
}
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 );
}
var Texture = {};
Texture.HandleLoadedTexture2D = function( image, texture, flipY ) {
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture( gl.TEXTURE_2D, texture );
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
if ( flipY != undefined && flipY == true )
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
gl.bindTexture( gl.TEXTURE_2D, null );
return texture;
}
Texture.LoadTexture2D = function( name ) {
var texture = gl.createTexture();
texture.image = new Image(64,64);
texture.image.setAttribute('crossorigin', 'anonymous');
texture.image.onload = function () {
var canvas = document.createElement( 'canvas' );
canvas.width = 512;
canvas.height = 256;
var context = canvas.getContext( '2d' );
context.drawImage( texture.image, 0, 0, canvas.width, canvas.height );
Texture.HandleLoadedTexture2D( canvas, texture, true )
}
texture.image.src = name;
return texture;
}
var ticks = 0;
function drawScene(){
var canvas = document.getElementById( "draw-canvas" );
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Camera.create();
Camera.vp = [canvas.width, canvas.height];
var currentTime = Date.now();
var deltaMS = currentTime - startTime;
ticks = ticks + 1;
gl.viewport( 0, 0, canvas.width, canvas.height );
//gl.clearColor( 0.0, 0.0, 0.0, 0.0 );
gl.disable( gl.DEPTH_TEST );
var texUnit = 1;
gl.activeTexture( gl.TEXTURE0 + texUnit );
gl.bindTexture( gl.TEXTURE_2D, textureObj );
var tex_loc = gl.getUniformLocation( progBG, "u_texture" );
gl.useProgram( progBG );
gl.uniform1i( tex_loc, texUnit );
var v_attr_inx = gl.getAttribLocation( progBG, "inPos" );
gl.bindBuffer( gl.ARRAY_BUFFER, bufRect );
gl.vertexAttribPointer( v_attr_inx, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( v_attr_inx );
gl.drawArrays( gl.TRIANGLE_FAN, 0, 4 );
gl.disableVertexAttribArray( v_attr_inx );
// set up draw shader
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShaderProgram.SetUniformM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentityMat44()
modelMat = RotateAxis( modelMat, CalcAng( currentTime, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( currentTime, 17.0 ), 1 );
ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", modelMat );
// draw scene
gl.clear( gl.DEPTH_BUFFER_BIT );
gl.enable( gl.DEPTH_TEST );
VertexBuffer.Draw( bufCube );
}
var startTime;
function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( currentTime, intervall ) {
return Fract( (currentTime - startTime) / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( currentTime, intervall, range ) {
var pos = self.Fract( (currentTime - startTime) / (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 ];
}
var sliderScale = 100.0
var gl;
var progDraw, progBG;
var bufCube = {};
var bufRect = {};
function sceneStart() {
var canvas = document.getElementById( "draw-canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var vp = [canvas.width, canvas.height];
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return;
progBG = ShaderProgram.Create(
[ { source : "background-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "background-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progBG.inPos = gl.getAttribLocation( progBG, "inPos" );
if ( progBG == 0 )
return;
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
progDraw.inCol = gl.getAttribLocation( progDraw, "inCol" );
if ( progDraw == 0 )
return;
//textureObj = Texture.LoadTexture2D( "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/supermario.jpg" );
textureObj = Texture.LoadTexture2D( "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/background.jpg" );
bufRect = gl.createBuffer()
gl.bindBuffer( gl.ARRAY_BUFFER, bufRect );
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [ -1, -1, 1, -1, 1, 1, -1, 1 ] ), gl.STATIC_DRAW );
// create cube
var cubePos = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 ];
var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ];
var cubePosData = [];
for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
}
var cubeNVData = [];
for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
var nv = [0, 0, 0];
for ( i2 = 0; i2 < 4; ++ i2 ) {
var i = i1 + i2;
nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
}
for ( i2 = 0; i2 < 4; ++ i2 )
cubeNVData.push( nv[0], nv[1], nv[2] );
}
var cubeColData = [];
for ( var is = 0; is < 6; ++ is ) {
for ( var ip = 0; ip < 4; ++ ip ) {
cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] );
}
}
var cubeInxData = [];
for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );
}
bufCube = VertexBuffer.Create(
[ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos },
//{ data : cubeNVData, attrSize : 3, attrLoc : progDraw.inNV },
{ data : cubeColData, attrSize : 3, attrLoc : progDraw.inCol } ],
cubeInxData );
startTime = Date.now();
setInterval(drawScene, 50);
}
<script id="background-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
vertPos = inPos;
gl_Position = vec4( inPos, 0.0, 1.0 );
}
</script>
<script id="background-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform sampler2D u_texture;
void main()
{
vec2 texCoord = vec2( vertPos.s, -vertPos.t ) * 0.5 + 0.5;
vec3 texColor = texture2D( u_texture, texCoord.st ).rgb;
gl_FragColor = vec4( texColor.rgb, 1.0 );
}
</script>
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec3 inPos;
attribute vec3 inCol;
varying vec3 vertCol;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
void main()
{
vertCol = inCol;
vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
vec4 viewPos = u_viewMat44 * modelPos;
gl_Position = u_projectionMat44 * viewPos;
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vertCol;
void main()
{
gl_FragColor = vec4( vertCol.rgb, 1.0 );
}
</script>
<body onload="sceneStart();">
<canvas id="draw-canvas" style="border: none;" width="100%" height="100%"></canvas>
</body>
Related
I've created a cube with a geometry shader with 14 vertex (Thanks to triangle strip). The problem now is how to place my texture coordinates. I have an image file that contains 3 textures :
Top face of the cube
Bottom face
left/right/front/back faces
I can't find a way to give a texture coordinate to each vertices (14). I always get texture displaying strangely.
Is it possible to set texture coordinates on a 14 vertex cube made with triangle strip ?
Is it possible to set texture coordinates on a 14 vertex cube made with triangle strip ?
At least you have to use separate vertices and attributes, for 2 edges of the cube. This means you need at least 8+2*2=12 different sets of vertex attributes.
x y z u v
0 : -1 1 1 0 0
1 : 1 1 1 1 0
2 : -1 -1 1 0 1
3 : 1 -1 1 1 1
4 : -1 -1 -1 0 0
5 : 1 -1 -1 1 0
6 : -1 1 -1 0 1
7 : 1 1 -1 1 1
8 : -1 1 -1 1 0
9 : -1 -1 -1 1 1
10 : 1 -1 -1 0 1
11 : 1 -1 1 0 0
The triangle strip may consist of 18 indices, where the triangle strip "restarts at the points 3 and 6. This is achieved by adding the indices to the list twice. This causes a triangles which are "invisible", because 2 points are equal:
[8, 9, 0, 2, 1, 3,
3, 2, 5, 4, 7, 6,
6, 0, 7, 1, 10, 11]
Note, to improve the pervormance, it is possibel to enable Face Culling. For the above indices, front faces are counterclockwise:
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
See the WebGL example, which demonstrates the mesh:
var readInput = true;
function changeEventHandler(event){
readInput = true;
}
(function loadscene() {
var gl, progDraw, vp_size;
var bufCube = {};
var tex_unit = 1;
function render(delteMS){
if ( readInput ) {
readInput = false;
// ...
}
Camera.create();
Camera.vp = vp_size;
gl.viewport( 0, 0, vp_size[0], vp_size[1] );
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.frontFace(gl.CCW)
gl.cullFace(gl.BACK)
gl.enable(gl.CULL_FACE)
// set up draw shader
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformI1( progDraw, "u_texture", tex_unit );
ShaderProgram.SetUniformM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShaderProgram.SetUniformM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentityMat44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", modelMat );
// draw scene
VertexBuffer.Draw( bufCube );
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];
}
function initScene() {
canvas = document.getElementById( "canvas");
gl = canvas.getContext( "experimental-webgl" );
//gl = canvas.getContext( "webgl2" );
if ( !gl )
return null;
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShaderProgram.AttributeIndex( progDraw, "inPos" );
progDraw.inUV = ShaderProgram.AttributeIndex( progDraw, "inUV" );
var cubePosData = [
-1, 1, 1,
1, 1, 1,
-1, -1, 1,
1, -1, 1,
-1, -1, -1,
1, -1, -1,
-1, 1, -1,
1, 1, -1,
-1, 1, -1,
-1, -1, -1,
1, -1, -1,
1, -1, 1 ];
var cubeTexData = [
0, 0,
1, 0,
0, 1,
1, 1,
0, 0,
1, 0,
0, 1,
1, 1,
1, 0,
1, 1,
0, 1,
0, 0 ];
cubeInxData = [
8, 9, 0, 2, 1, 3,
3, 2, 5, 4, 7, 6,
6, 0, 7, 1, 10, 11];
bufCube = VertexBuffer.Create(
[ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos },
{ data : cubeTexData, attrSize : 2, attrLoc : progDraw.inUV } ],
cubeInxData );
bufCube.primitve_type = gl.TRIANGLE_STRIP;
Texture.LoadTexture2D( tex_unit, "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/test1_texture.bmp" );
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 Texture = {};
Texture.HandleLoadedTexture2D = function( texture, flipY ) {
gl.activeTexture( gl.TEXTURE0 + texture.unit );
gl.bindTexture( gl.TEXTURE_2D, texture.obj );
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image );
if ( flipY != undefined && flipY == true )
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
return texture;
}
Texture.LoadTexture2D = function( unit, name ) {
var texture = {};
texture.obj = gl.createTexture();
texture.unit = unit;
texture.image = new Image();
texture.image.setAttribute('crossorigin', 'anonymous');
texture.image.onload = function () {
Texture.HandleLoadedTexture2D( texture, true )
}
texture.image.src = name;
return texture;
}
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 prog = {}
prog.progObj = this.LinkProgram( shaderObjs )
if ( prog.progObj ) {
prog.attribIndex = {};
var noOfAttributes = gl.getProgramParameter( prog.progObj, gl.ACTIVE_ATTRIBUTES );
for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
var name = gl.getActiveAttrib( prog.progObj, i_n ).name;
prog.attribIndex[name] = gl.getAttribLocation( prog.progObj, name );
}
prog.unifomLocation = {};
var noOfUniforms = gl.getProgramParameter( prog.progObj, gl.ACTIVE_UNIFORMS );
for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
var name = gl.getActiveUniform( prog.progObj, i_n ).name;
prog.unifomLocation[name] = gl.getUniformLocation( prog.progObj, name );
}
}
return prog;
}
ShaderProgram.AttributeIndex = function( prog, name ) { return prog.attribIndex[name]; }
ShaderProgram.UniformLocation = function( prog, name ) { return prog.unifomLocation[name]; }
ShaderProgram.Use = function( prog ) { gl.useProgram( prog.progObj ); }
ShaderProgram.SetUniformI1 = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1i( prog.unifomLocation[name], val ); }
ShaderProgram.SetUniformF1 = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1f( prog.unifomLocation[name], val ); }
ShaderProgram.SetUniformF2 = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform2fv( prog.unifomLocation[name], arr ); }
ShaderProgram.SetUniformF3 = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform3fv( prog.unifomLocation[name], arr ); }
ShaderProgram.SetUniformF4 = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform4fv( prog.unifomLocation[name], arr ); }
ShaderProgram.SetUniformM33 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix3fv( prog.unifomLocation[name], false, mat ); }
ShaderProgram.SetUniformM44 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix4fv( prog.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( bufObj.primitve_type, 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();
})();
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;
attribute vec3 inPos;
attribute vec2 inUV;
varying vec3 vertPos;
varying vec2 vertUV;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
void main()
{
mat4 mv = u_viewMat44 * u_modelMat44;
vertUV = inUV;
vec4 viewPos = mv * vec4( inPos, 1.0 );
vertPos = viewPos.xyz;
gl_Position = u_projectionMat44 * viewPos;
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vertPos;
varying vec2 vertUV;
uniform sampler2D u_texture;
void main()
{
vec3 color = texture2D( u_texture, vertUV.st ).rgb;
gl_FragColor = vec4( color.rgb+0.1, 1.0 );
}
</script>
<canvas id="canvas"></canvas>
So you do have to unwrap it in a similar fashion that Rabbid76 mentions, however you don't need invisible triangles or an index buffer for the triangle strip mode drawing.
Here are the values I got:
position UV
(-1f, 1f, 1f), (0, 0)
( 1f, 1f, 1f), (1, 0)
(-1f, -1f, 1f), (0, 1)
( 1f, -1f, 1f), (1, 1)
( 1f, -1f, -1f), (1, 0)
( 1f, 1f, 1f), (0, 1)
( 1f, 1f, -1f), (0, 0)
(-1f, 1f, 1f), (1, 1)
(-1f, 1f, -1f), (1, 0)
(-1f, -1f, 1f), (0, 1)
(-1f, -1f, -1f), (0, 0)
( 1f, -1f, -1f), (1, 0)
(-1f, 1f, -1f), (0, 1)
( 1f, 1f, -1f), (1, 1)
It's also worth noting that there are 14 vertices listed here and 12 triangles
I'm just getting started with shaders in openframeworks, and am trying to write a fragment shader that changes the color of the fragment based on what angle its viewed from. For example, given a rectangle, if viewed from head on (camera is parallel to normal) it would be red, but if viewed from a side, it turns blue.
For a sphere, similarly, its apparent middle would be red (as the camera is perpendicular to those faces), while the apparent edges should be blue.
I figured I could set the fragment color based on what the viewing angle relative to the normal is, however, I'm having trouble finding any GLSL input variables that gives me the requisite information. gl_FragCoord doesn't seem to work, as it only gives position relative to the window, and gl_PointCoord gives the position relative to the model itself, but not from where its viewed.
What would be the straightforward way to get a viewing angle/ accomplish this effect?
I'm using GLSL version 1.2, and loading the shaders with openframeworks 0.9.8.
For a sphere, similarly, its apparent middle would be red (as the camera is perpendicular to those faces), while the apparent edges should be blue.
To achieve what you want I recommend to calculate the intensity of a perfect lambertian diffuse light, which lits the scene from the front.
It is easy to do this calcualtions in viewspace.
Lambertian reflection is often used as a model for diffuse reflection. This technique causes all closed polygons (such as a triangle within a 3D mesh) to reflect light equally in all directions when rendered. The diffusion coefficient is calculated from the angle between the normal vector and the light vector.
f_Lambertian = max( 0.0, dot( N, L ) )
where N is the normal vector of the surface, and L is the vector towards to the light source.
See How does this faking the light work on aerotwist? for detailed information.
In the view space of a Right-handed system, Z-axis points out of the view (Note in a right hand system the Z-Axis is the cross product of the X-Axis and the Y-Axis).
This means the light intensity can be calculated by the dot product of the normal vector in view space and the "light" vector in view space, which is the view space Z-axis (0, 0, 1).
The following shader colors the the fragments which have a normal vector that points to the view, in red. The fragments whose normal vector point sidewards ar tint blue:
Vertex shader:
in vec3 inPos;
in vec3 inNV;
out vec3 viewNV;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
void main()
{
viewNV = mat3(u_viewMat44 * u_modelMat44) * inNV;
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );
gl_Position = u_projectionMat44 * pos;
}
Fragment shader:
in vec3 viewNV;
void main()
{
vec3 N = normalize(viewNV);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);
vec3 color = vec3(NdotL, 0.0, 1.0-NdotL);
gl_FragColor = vec4( color.rgb, 1.0 );
}
See the WebGL example, which demonstrates the effect on a rotating cube:
(function loadscene() {
var gl, progDraw, vp_size;
var bufCube = {};
function render(delteMS){
Camera.create();
Camera.vp = vp_size;
gl.viewport( 0, 0, vp_size[0], vp_size[1] );
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 );
// set up draw shader
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShaderProgram.SetUniformM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentityMat44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", modelMat );
// draw scene
VertexBuffer.Draw( bufCube );
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];
}
function initScene() {
canvas = document.getElementById( "canvas");
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return null;
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShaderProgram.AttributeIndex( progDraw, "inPos" );
progDraw.inNV = ShaderProgram.AttributeIndex( progDraw, "inNV" );
// create cube
var cubePos = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 ];
var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ];
var cubePosData = [];
for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
}
var cubeNVData = [];
for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
var nv = [0, 0, 0];
for ( i2 = 0; i2 < 4; ++ i2 ) {
var i = i1 + i2;
nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
}
for ( i2 = 0; i2 < 4; ++ i2 )
cubeNVData.push( nv[0], nv[1], nv[2] );
}
var cubeColData = [];
for ( var is = 0; is < 6; ++ is ) {
for ( var ip = 0; ip < 4; ++ ip ) {
cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] );
}
}
var cubeInxData = [];
for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );
}
bufCube = VertexBuffer.Create(
[ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos },
{ data : cubeNVData, attrSize : 3, attrLoc : progDraw.inNV } ],
cubeInxData );
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.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 prog = {}
prog.progObj = this.LinkProgram( shaderObjs )
if ( prog.progObj ) {
prog.attribIndex = {};
var noOfAttributes = gl.getProgramParameter( prog.progObj, gl.ACTIVE_ATTRIBUTES );
for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
var name = gl.getActiveAttrib( prog.progObj, i_n ).name;
prog.attribIndex[name] = gl.getAttribLocation( prog.progObj, name );
}
prog.unifomLocation = {};
var noOfUniforms = gl.getProgramParameter( prog.progObj, gl.ACTIVE_UNIFORMS );
for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
var name = gl.getActiveUniform( prog.progObj, i_n ).name;
prog.unifomLocation[name] = gl.getUniformLocation( prog.progObj, name );
}
}
return prog;
}
ShaderProgram.AttributeIndex = function( prog, name ) { return prog.attribIndex[name]; }
ShaderProgram.UniformLocation = function( prog, name ) { return prog.unifomLocation[name]; }
ShaderProgram.Use = function( prog ) { gl.useProgram( prog.progObj ); }
ShaderProgram.SetUniformI1 = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1i( prog.unifomLocation[name], val ); }
ShaderProgram.SetUniformF1 = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1f( prog.unifomLocation[name], val ); }
ShaderProgram.SetUniformF2 = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform2fv( prog.unifomLocation[name], arr ); }
ShaderProgram.SetUniformF3 = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform3fv( prog.unifomLocation[name], arr ); }
ShaderProgram.SetUniformF4 = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform4fv( prog.unifomLocation[name], arr ); }
ShaderProgram.SetUniformM33 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix3fv( prog.unifomLocation[name], false, mat ); }
ShaderProgram.SetUniformM44 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix4fv( prog.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();
})();
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;
attribute vec3 inPos;
attribute vec3 inNV;
varying vec3 viewNV;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
void main()
{
viewNV = mat3(u_viewMat44 * u_modelMat44) * inNV;
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );
gl_Position = u_projectionMat44 * pos;
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 viewNV;
void main()
{
vec3 N = normalize(viewNV);
vec3 Z = vec3(0.0, 0.0, 1.0);
float NdotZ = dot(N, Z);
vec3 color = vec3(NdotZ, 0.0, 1.0-NdotZ);
gl_FragColor = vec4( color.rgb, 1.0 );
}
</script>
<canvas id="canvas" style="border: none;" width="100%" height="100%"></canvas>
I'm using some shader code to create multi-step linear gradients by mixing colors. It works great. I have a radial shader which uses distance. Works fine but I dont know to adapt it to multi-step.
How could I approach it?
Linear Gradient http://glslsandbox.com/e#39992.0
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
void main( void ) {
float y = gl_FragCoord.y / resolution.y;
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
float step1 = 0.0;
float step2 = 0.33;
float step3 = 0.66;
float step4 = 1.0;
vec4 color = mix(white, red, smoothstep(step1, step2, y));
color = mix(color, blue, smoothstep(step2, step3, y));
color = mix(color, green, smoothstep(step3, step4, y));
gl_FragColor = color;
}
Simple radial gradient https://www.shadertoy.com/view/4tjSWh
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float d = distance(iResolution.xy*0.5,fragCoord.xy)*(sin(1.0)+1.5)*0.003;
fragColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.0, 0.0, 0.0, 1.0), d);
}
You simply have to calcualte the distance from the current fragment to the center of the viewport. For this you have to calculate the fragment position in normalized device coordinates. You can use the distance to the center in the same way, as you did it with the y coordinate in the "strip" shader:
vec2 pos_ndc = 2.0 * gl_FragCoord.xy / resolution.xy - 1.0;
float dist = length(pos_ndc);
Your shader should look somehow like this:
void main()
{
vec2 pos_ndc = 2.0 * gl_FragCoord.xy / resolution.xy - 1.0;
float dist = length(pos_ndc);
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
float step1 = 0.0;
float step2 = 0.33;
float step3 = 0.66;
float step4 = 1.0;
vec4 color = mix(white, red, smoothstep(step1, step2, dist));
color = mix(color, blue, smoothstep(step2, step3, dist));
color = mix(color, green, smoothstep(step3, step4, dist));
gl_FragColor = color;
}
Preview:
See the code snippet:
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 = "";
var node = shaderScript.firstChild;
while (node) {
if (node.nodeType == 3) source += node.textContent;
node = node.nextSibling;
}
}
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 : 0;
}
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 : 0;
}
function drawScene(){
var canvas = document.getElementById( "ogl-canvas" );
var vp = [canvas.width, canvas.height];
gl.viewport( 0, 0, canvas.width, canvas.height );
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 );
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformF2( progDraw, "resolution", [256.0, 256.0] )
gl.enableVertexAttribArray( progDraw.inPos );
gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
gl.disableVertexAttribArray( progDraw.pos );
}
var gl;
var prog;
var bufObj = {};
function sceneStart() {
var canvas = document.getElementById( "ogl-canvas");
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return;
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
if ( prog == 0 )
return;
var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
var inx = [ 0, 1, 2, 0, 2, 3 ];
bufObj.pos = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
bufObj.inx = gl.createBuffer();
bufObj.inx.len = inx.length;
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW );
setInterval(drawScene, 50);
}
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
vertPos = inPos;
gl_Position = vec4( inPos.xy, 0.0, 1.0 );
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform vec2 resolution;
void main()
{
vec2 pos_ndc = 2.0 * gl_FragCoord.xy / resolution.xy - 1.0;
float dist = length(pos_ndc);
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);
vec4 green = vec4(0.0, 1.0, 0.0, 1.0);
float step1 = 0.0;
float step2 = 0.33;
float step3 = 0.66;
float step4 = 1.0;
vec4 color = mix(white, red, smoothstep(step1, step2, dist));
color = mix(color, blue, smoothstep(step2, step3, dist));
color = mix(color, green, smoothstep(step3, step4, dist));
gl_FragColor = color;
}
</script>
<body onload="sceneStart();">
<canvas id="ogl-canvas" style="border: none;" width="256" height="256"></canvas>
</body>
I am trying to render multiple particles in my frag shader but only two of the 600 is being displayed. Could anybody help me to understand what is going on?
shader.frag
#version 120
uniform sampler2DRect texture2;
uniform float iTime;
uniform vec2 iResolution;
#define M_PI 3.1415926535897932384626433832795
float random(vec2 co){
float a = 12.9898;
float b = 78.233;
float c = 43758.5453;
float dt= dot(co.xy ,vec2(a,b));
float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
void main(void){
vec4 outColor = vec4(0.0, 0.0,0.0,1.0);
float time = iTime * 0.1;
vec2 uvNorm = gl_FragCoord.xy / iResolution.xy;
vec2 uv = -0.5 + 1.0 * uvNorm;
uv /= vec2(iResolution.y / iResolution.x, 1.);
for(float i=0.0; i<600.0 ;i++){
float f1 = mod(i * 0.101213, 0.28);
float fft = texture2DRect(texture2, vec2(f1)).x;
float r = (fft/10.0);
float a = random(vec2(i))*(M_PI*2.);
vec2 center = vec2(cos(a), sin(a)) * r/1.2;
float dist = length(uv - center);
float birghtness = 1./pow(0.01 + dist*250., 2.);
vec3 color = vec3(fft-0.1, 1.0, fft-0.2);
vec3 col = color* birghtness/2.0 * fft * 2.;
col += color * birghtness * fft * 1.5;
outColor.rgb += col;
}
gl_FragColor = outColor;
}
I am fairly new to openGL and I have been having some issues regarding different version of openGL before. Could this be something similar?
The particles crowd together in a small area. Reduce the number of particles from 600 to e.g. 50 and you can see the separated particles.
for(float i=0.0; i<50.0 ;i++){
....
Further the area where the particles are displayed is very small. You scale the radius, to enlarge the area where the particles are drawn:
vec2 uvNorm = gl_FragCoord.xy / iResolution.xy;
vec2 uv = (uvNorm - 0.5) * vec2(iResolution.x / iResolution.y, 1.0);
uv *= 0.3; // scale
Note, you don't ues the variable iTime/time. This may cause that the particles crowd together, because you only display an initial state.
I don't know how the time was used in the initial algorithm, because you didn't post a reference. But I put it somewhere, see the result in the code snippet:
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.CompileShader = function( source, shaderStage ) {
var shaderScript = document.getElementById(source);
if (shaderScript) {
source = "";
var node = shaderScript.firstChild;
while (node) {
if (node.nodeType == 3) source += node.textContent;
node = node.nextSibling;
}
}
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 : 0;
}
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 : 0;
}
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 );
}
var Texture = {};
Texture.HandleLoadedTexture2D = function( image, texture, flipY ) {
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture( gl.TEXTURE_2D, texture );
gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
if ( flipY != undefined && flipY == true )
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT );
gl.bindTexture( gl.TEXTURE_2D, null );
return texture;
}
Texture.LoadTexture2D = function( name ) {
var texture = gl.createTexture();
texture.image = new Image();
texture.image.setAttribute('crossorigin', 'anonymous');
texture.image.onload = function () {
Texture.HandleLoadedTexture2D( texture.image, texture, true )
}
texture.image.src = name;
return texture;
}
var timing = {};
timing.prevTimeAbs = 0;
timing.pause = 0;
timing.deltaTimeLastMs = 0;
timing.deltaTimeAbsMs = 0;
timing.init = function() {
this.prevTimeAbs = Date.now();
this.pause = 0;
this.deltaTimeLastMs = 0;
this.deltaTimeAbsMs = 0;
};
timing.calcDeltaTimes = function() {
var currentTimeAbs = Date.now();
var delta = currentTimeAbs - this.prevTimeAbs;
this.prevTimeAbs = currentTimeAbs;
this.deltaTimeLastMs = this.pause == 0 ? delta : 0;
this.deltaTimeAbsMs += this.deltaTimeLastMs;
return this.deltaTimeAbsMs;
};
function drawScene(){
var canvas = document.getElementById( "camera-canvas" );
var vp = [canvas.width, canvas.height];
var pastTime = timing.calcDeltaTimes() / 1000.0;
gl.viewport( 0, 0, canvas.width, canvas.height );
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 );
var texUnit = 0;
gl.activeTexture( gl.TEXTURE0 + texUnit );
gl.bindTexture( gl.TEXTURE_2D, textureObj );
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformF1( progDraw, "iTime", pastTime );
ShaderProgram.SetUniformF2( progDraw, "iResolution", vp );
ShaderProgram.SetUniformI1( progDraw, "texture2", texUnit );
VertexBuffer.Draw( bufRect );
}
var gl;
var prog;
var bufObj = {};
var textureObj;
var maskTextureObj;
function sceneStart() {
var canvas = document.getElementById( "camera-canvas");
gl = canvas.getContext( "experimental-webgl" );
//gl = canvas.getContext( "webgl2" );
if ( !gl )
return;
var texCX = 128;
var texCY = 128;
var texPlan = [];
for (ix = 0; ix < texCX; ++ix) {
for (iy = 0; iy < texCY; ++iy) {
var val_x = Math.sin( Math.PI * 6.0 * ix / texCX )
var val_y = Math.sin( Math.PI * 6.0 * iy / texCY )
texPlan.push( 128 + 127 * val_x, 63, 128 + 127 * val_y, 255 );
}
}
textureObj = Texture.LoadTexture2D( "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/supermario.jpg" );
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
if ( prog == 0 )
return;
bufRect = VertexBuffer.Create(
[ { data : [ -1, -1, 1, -1, 1, 1, -1, 1 ], attrSize : 2, attrLoc : progDraw.inPos } ],
[ 0, 1, 2, 0, 2, 3 ] );
timing.init();
setInterval(drawScene, 50);
}
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
vertPos = inPos;
gl_Position = vec4( inPos, 0.0, 1.0 );
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform float iTime;
uniform vec2 iResolution;
uniform sampler2D texture2;
#define M_PI 3.1415926535897932384626433832795
float random(vec2 co){
float a = 12.9898;
float b = 78.233;
float c = 43758.5453;
float dt= dot(co.xy ,vec2(a,b));
float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
void main(void){
vec4 outColor = vec4(0.0, 0.0,0.0,1.0);
float time = iTime * 0.1;
vec2 uvNorm = gl_FragCoord.xy / iResolution.xy;
vec2 uv = (uvNorm - 0.5) * vec2(iResolution.x / iResolution.y, 1.0);
uv *= 0.25; // scale
//for(float i=0.0; i<600.0 ;i++){
for(float i=0.0; i<30.0 ;i++){
float f1 = mod(sin(time) * i * 0.101213, 0.28);
float fft = texture2D(texture2, vec2(f1)).x;
float r = (fft/10.0);
float a = random(vec2(i,i))*(M_PI*2.);
vec2 center = vec2(cos(a), sin(a)) * r/1.2;
float dist = length(uv - center);
float birghtness = 1./pow(0.01 + dist*250., 2.);
vec3 color = vec3(fft-0.1, 1.0, fft-0.2);
vec3 col = color* birghtness/2.0 * fft * 2.;
col += color * birghtness * fft * 1.5;
outColor.rgb += col;
}
gl_FragColor = outColor;
}
</script>
<body onload="sceneStart();">
<canvas id="camera-canvas" style="border: none;" width="512" height="512"></canvas>
</body>
When using the OpenGL fixed function pipeline for vertex setup, how a fragment program looks like that is compatible to the fixed function vertex setup? I guess that usually depends on the number of light sources and texture layers etc.. So for example how does a simple non-texture one-lightsource goraud shading fragment program look like that replaces GL's fixed function shader?
You might be interested in having a look at ShaderGen. From the description:
ShaderGen is a program that can automatically produce shaders that mimic the results of a set of fixed function state. This tool was used to verify the fixed function shader code that is described in Chapter 9.
By the way, you will most likely encounter subtle driver problems if you mix fixed function and GLSL. This is not the "most used way" to do shading so it has a good change of having bugs (at least it did when I tried it). If what you need works, great! Otherwise, you're likely much better off writing the vertex and fragment shaders in GLSL. From what I heard, the driver of modern GPUs uses shaders under the hood anyway to do fixed function.
While Gouraud shading calculates the light in the the vertex shader, Phong shading calculates the light in the fragment shader.
The standard OpenGL light model is a Gouraud shading model, with a Blinn-Phong light model (Do not confuse with Phong shading).
The standard OpenGL Blinn-Phong light model is calcualted like this:
Ka ... ambient material
Kd ... difusse material
Ks ... specular material
La ... ambient light
Ld ... diffuse light
Ls ... specular light
sh ... shininess
N ... norlmal vector
L ... light vector (from the vertex postion to the light)
V ... view vector (from the vertex psotion to the eye)
Id = max(dot(N, L), 0.0);
H = normalize(V + L);
NdotH = max(dot(N, H), 0.0);
Is = (sh + 2.0) * pow(NdotH, sh) / (2.0 * 3.14159265);
fs = Ka*La + Id*Kd*Ld + Is*Ks*Ls;
The following function calculates a single directional Blinn-Phong light source:
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;
}
See also the answers to the following questions:
How does this faking the light work on aerotwist?
This function can be applied to a vertex shader, as to a fragment shader, too.
The full coding of a gouraud shader program and a phong shader program can be found in the following WebGL example:
(function loadscene() {
var resize, gl, gouraudDraw, phongDraw, vp_size;
var bufCube, bufSphere, bufTorus;
var sliderScale = 100.0;
function render(delteMS){
var ambient_col = hexToRgb( document.getElementById( "ambient_col" ).value );
var diffuse_col = hexToRgb( document.getElementById( "diffuse_col" ).value );
var specular_col = hexToRgb( document.getElementById( "specular_col" ).value );
var ambient = document.getElementById( "ambient" ).value / sliderScale;
var diffuse = document.getElementById( "diffuse" ).value / sliderScale;
var specular = document.getElementById( "specular" ).value / sliderScale;
var shininess = document.getElementById( "shininess" ).value;
var ambientCol = [ambient_col.r*ambient/256.0, ambient_col.g*ambient/256.0, ambient_col.b*ambient/256.0];
var diffuseCol = [diffuse_col.r*diffuse/256.0, diffuse_col.g*diffuse/256.0, diffuse_col.b*diffuse/256.0];
var specularCol = [specular_col.r*ambient/256.0, specular_col.g*ambient/256.0, specular_col.b*ambient/256.0];
var form = document.getElementById( "form" ).value;
var shading = document.getElementById( "shading" ).value;
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
bufObj = form == 0 ? bufCube : form == 1 ? bufSphere : bufTorus;
VertexBuffer.Draw( bufObj );
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() {
document.getElementById( "ambient_col" ).value = "#FFFFFF";
document.getElementById( "diffuse_col" ).value = "#FFFFFF";
document.getElementById( "specular_col" ).value = "#FFFFFF";
document.getElementById( "ambient" ).value = 0.2 * sliderScale;
document.getElementById( "diffuse" ).value = 0.6 * sliderScale;
document.getElementById( "specular" ).value = 0.8 * sliderScale;
document.getElementById( "shininess" ).value = 25.0;
document.getElementById( "shading" ).value = 0;
document.getElementById( "form" ).value = 1;
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 cubePos = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 ];
var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ];
var cubePosData = [];
for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
}
var cubeNVData = [];
for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
var nv = [0, 0, 0];
for ( i2 = 0; i2 < 4; ++ i2 ) {
var i = i1 + i2;
nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
}
for ( i2 = 0; i2 < 4; ++ i2 )
cubeNVData.push( nv[0], nv[1], nv[2] );
}
var cubeColData = [];
for ( var is = 0; is < 6; ++ is ) {
for ( var ip = 0; ip < 4; ++ ip ) {
cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] );
}
}
var cubeInxData = [];
for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );
}
bufCube = VertexBuffer.Create(
[ { data : cubePosData, attrSize : 3, attrLoc : gouraudDraw.inPos },
{ data : cubeNVData, attrSize : 3, attrLoc : gouraudDraw.inNV },
{ data : cubeColData, attrSize : 3, attrLoc : gouraudDraw.inCol } ],
cubeInxData );
// create sphere
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 );
sphere_col.push( 0.75, 0.75, 0.75 );
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_col.push( 0.75, 0.75, 0.75 );
}
}
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 );
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 );
// create torus
var circum_size = 32, tube_size = 32;
var rad_circum = 1.4;
var rad_tube = 0.6;
var torus_pts = [];
var torus_nv = [];
var torus_col = [];
var torus_inx = [];
var col = [1, 0.5, 0.0];
for ( var i_c = 0; i_c < circum_size; ++ i_c ) {
var center = [
Math.cos(2 * Math.PI * i_c / circum_size),
Math.sin(2 * Math.PI * i_c / circum_size) ]
for ( var i_t = 0; i_t < tube_size; ++ i_t ) {
var tubeX = Math.cos(2 * Math.PI * i_t / tube_size)
var tubeY = Math.sin(2 * Math.PI * i_t / tube_size)
var pt = [
center[0] * ( rad_circum + tubeX * rad_tube ),
center[1] * ( rad_circum + tubeX * rad_tube ),
tubeY * rad_tube ]
var nv = [ pt[0] - center[0] * rad_tube, pt[1] - center[1] * rad_tube, tubeY * rad_tube ]
torus_pts.push( pt[0], pt[1], pt[2] );
torus_nv.push( nv[0], nv[1], nv[2] );
torus_col.push( col[0], col[1], col[2] );
var i_cn = (i_c+1) % circum_size
var i_tn = (i_t+1) % tube_size
var i_c0 = i_c * tube_size;
var i_c1 = i_cn * tube_size;
torus_inx.push( i_c0+i_t, i_c1+i_t, i_c0+i_tn, i_c0+i_tn, i_c1+i_t, i_c1+i_tn )
}
}
bufTorus = VertexBuffer.Create(
[ { data : torus_pts, attrSize : 3, attrLoc : gouraudDraw.inPos },
{ data : torus_nv, attrSize : 3, attrLoc : gouraudDraw.inNV },
{ data : torus_col, attrSize : 3, attrLoc : gouraudDraw.inCol } ],
torus_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 );
}
function hexToRgb (hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
initScene();
})();
<style>
html,body { 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>
<div>
<form id="gui" name="inputs">
<table>
<tr> <td> <font color= #CCF>ambient</font> </td>
<td> <input type="color" id="ambient_col"></td>
<td> <input type="range" id="ambient" min="0" max="100" value="0"/></td> </tr>
<tr> <td> <font color= #CCF>diffuse</font> </td>
<td> <input type="color" id="diffuse_col"></td>
<td> <input type="range" id="diffuse" min="0" max="100" value="0"/></td> </tr>
<tr> <td> <font color= #CCF>specular</font> </td>
<td> <input type="color" id="specular_col"></td>
<td> <input type="range" id="specular" min="0" max="100" value="0"/></td> </tr>
<tr> <td> <font color= #CCF>shininess</font> </td>
<td> <input type="range" id="shininess" min="0" max="100" value="0"/></td> </tr>
<tr> <td> <font color= #CCF>form</font> </td> <td>
<select id="form">>
<option value="0">cube</option>
<option value="1">sphere</option>
<option value="2">torus</option>
</select>
</td> </tr>
<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>
</table>
</form>
</div>
<canvas id="canvas" style="border: none;"></canvas>
Well, in the normal GL fixed function pipeline, lighting and goraud shading are done entirely in the vertex stage of the pipeline (with a little bit of help from the rasterizer), so if you have no texturing, the fragment shader becomes a simple pass-through (gl_FragColor = gl_Color;) and doesn't actually do anything.
Now you CAN do per-fragment lighting, by writing your own fragment shader that does it, and it may even make sense to do that if you want directional spotlights or some such, but that has nothing to do with the fixed function pipeline.