Diffuse light with OpenGL GLSL












1















Currently, my OpenGL scene does not make use of any lights, it uses only wireframes which have a fixed colour - which works fine for me.



Now I want to add a second mode, where the wireframes are shown as (semi)solid models. For this, I need some lighting. Lighting should be quite easy, just some global directional light with fixed colour and direction.



So two questions: as far as I understand, I can do this within my fragment shader completely - is this correct?



And: My current fragment shader is quite simple:



#version 130
uniform vec4 inputColor;
out vec4 FragColor;
void main()
{
FragColor=inputColor;
}


What has to be added here to let my geometry be lit by a global directional light (but not by some additional point light sources)?










share|improve this question

























  • An ambient light does not have any direction. Ambient means that every pixel in the scene gets the same amount of light from the lightsource. What you are looking for is a diffuse directional light.

    – BDL
    Nov 23 '18 at 9:29











  • @BDL sorry, you are correct, edit made, I mean directional light!

    – Elmi
    Nov 23 '18 at 9:34






  • 2





    @Elmi At least you need the normal vector of the fragment.

    – Rabbid76
    Nov 23 '18 at 9:38











  • @Rabbid76 my geometries do not have any normal coordinates - is there a chance to auto-generate them right in the fragment shader?

    – Elmi
    Nov 23 '18 at 11:22











  • @Elmi see simple complete C++ GL+VAO/VBO+GLSL exmple for simple lighting ... using normal shading + ambient

    – Spektre
    Nov 23 '18 at 12:03


















1















Currently, my OpenGL scene does not make use of any lights, it uses only wireframes which have a fixed colour - which works fine for me.



Now I want to add a second mode, where the wireframes are shown as (semi)solid models. For this, I need some lighting. Lighting should be quite easy, just some global directional light with fixed colour and direction.



So two questions: as far as I understand, I can do this within my fragment shader completely - is this correct?



And: My current fragment shader is quite simple:



#version 130
uniform vec4 inputColor;
out vec4 FragColor;
void main()
{
FragColor=inputColor;
}


What has to be added here to let my geometry be lit by a global directional light (but not by some additional point light sources)?










share|improve this question

























  • An ambient light does not have any direction. Ambient means that every pixel in the scene gets the same amount of light from the lightsource. What you are looking for is a diffuse directional light.

    – BDL
    Nov 23 '18 at 9:29











  • @BDL sorry, you are correct, edit made, I mean directional light!

    – Elmi
    Nov 23 '18 at 9:34






  • 2





    @Elmi At least you need the normal vector of the fragment.

    – Rabbid76
    Nov 23 '18 at 9:38











  • @Rabbid76 my geometries do not have any normal coordinates - is there a chance to auto-generate them right in the fragment shader?

    – Elmi
    Nov 23 '18 at 11:22











  • @Elmi see simple complete C++ GL+VAO/VBO+GLSL exmple for simple lighting ... using normal shading + ambient

    – Spektre
    Nov 23 '18 at 12:03
















1












1








1








Currently, my OpenGL scene does not make use of any lights, it uses only wireframes which have a fixed colour - which works fine for me.



Now I want to add a second mode, where the wireframes are shown as (semi)solid models. For this, I need some lighting. Lighting should be quite easy, just some global directional light with fixed colour and direction.



So two questions: as far as I understand, I can do this within my fragment shader completely - is this correct?



And: My current fragment shader is quite simple:



#version 130
uniform vec4 inputColor;
out vec4 FragColor;
void main()
{
FragColor=inputColor;
}


What has to be added here to let my geometry be lit by a global directional light (but not by some additional point light sources)?










share|improve this question
















Currently, my OpenGL scene does not make use of any lights, it uses only wireframes which have a fixed colour - which works fine for me.



Now I want to add a second mode, where the wireframes are shown as (semi)solid models. For this, I need some lighting. Lighting should be quite easy, just some global directional light with fixed colour and direction.



So two questions: as far as I understand, I can do this within my fragment shader completely - is this correct?



And: My current fragment shader is quite simple:



#version 130
uniform vec4 inputColor;
out vec4 FragColor;
void main()
{
FragColor=inputColor;
}


What has to be added here to let my geometry be lit by a global directional light (but not by some additional point light sources)?







opengl glsl opengl-3 lighting light






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 23 '18 at 12:13









Rabbid76

42.4k123353




42.4k123353










asked Nov 23 '18 at 9:26









ElmiElmi

2,22574595




2,22574595













  • An ambient light does not have any direction. Ambient means that every pixel in the scene gets the same amount of light from the lightsource. What you are looking for is a diffuse directional light.

    – BDL
    Nov 23 '18 at 9:29











  • @BDL sorry, you are correct, edit made, I mean directional light!

    – Elmi
    Nov 23 '18 at 9:34






  • 2





    @Elmi At least you need the normal vector of the fragment.

    – Rabbid76
    Nov 23 '18 at 9:38











  • @Rabbid76 my geometries do not have any normal coordinates - is there a chance to auto-generate them right in the fragment shader?

    – Elmi
    Nov 23 '18 at 11:22











  • @Elmi see simple complete C++ GL+VAO/VBO+GLSL exmple for simple lighting ... using normal shading + ambient

    – Spektre
    Nov 23 '18 at 12:03





















  • An ambient light does not have any direction. Ambient means that every pixel in the scene gets the same amount of light from the lightsource. What you are looking for is a diffuse directional light.

    – BDL
    Nov 23 '18 at 9:29











  • @BDL sorry, you are correct, edit made, I mean directional light!

    – Elmi
    Nov 23 '18 at 9:34






  • 2





    @Elmi At least you need the normal vector of the fragment.

    – Rabbid76
    Nov 23 '18 at 9:38











  • @Rabbid76 my geometries do not have any normal coordinates - is there a chance to auto-generate them right in the fragment shader?

    – Elmi
    Nov 23 '18 at 11:22











  • @Elmi see simple complete C++ GL+VAO/VBO+GLSL exmple for simple lighting ... using normal shading + ambient

    – Spektre
    Nov 23 '18 at 12:03



















An ambient light does not have any direction. Ambient means that every pixel in the scene gets the same amount of light from the lightsource. What you are looking for is a diffuse directional light.

– BDL
Nov 23 '18 at 9:29





An ambient light does not have any direction. Ambient means that every pixel in the scene gets the same amount of light from the lightsource. What you are looking for is a diffuse directional light.

– BDL
Nov 23 '18 at 9:29













@BDL sorry, you are correct, edit made, I mean directional light!

– Elmi
Nov 23 '18 at 9:34





@BDL sorry, you are correct, edit made, I mean directional light!

– Elmi
Nov 23 '18 at 9:34




2




2





@Elmi At least you need the normal vector of the fragment.

– Rabbid76
Nov 23 '18 at 9:38





@Elmi At least you need the normal vector of the fragment.

– Rabbid76
Nov 23 '18 at 9:38













@Rabbid76 my geometries do not have any normal coordinates - is there a chance to auto-generate them right in the fragment shader?

– Elmi
Nov 23 '18 at 11:22





@Rabbid76 my geometries do not have any normal coordinates - is there a chance to auto-generate them right in the fragment shader?

– Elmi
Nov 23 '18 at 11:22













@Elmi see simple complete C++ GL+VAO/VBO+GLSL exmple for simple lighting ... using normal shading + ambient

– Spektre
Nov 23 '18 at 12:03







@Elmi see simple complete C++ GL+VAO/VBO+GLSL exmple for simple lighting ... using normal shading + ambient

– Spektre
Nov 23 '18 at 12:03














1 Answer
1






active

oldest

votes


















4














To calculate a diffuse light or even specular hight lights, you have to know the normal vector of the surface respectively fragment. The diffuse light radiance depends on the the direction of the incident light - see How does this faking the light work on aerotwist?. Specular highlights additionally depend on the direct of view.



If you do not have normal vectors, then the surface normal vector can be calculated approximately by the partial derivative of the view space position in the fragment shader. The partial derivative can be get by the functions dFdx and dFdy.



With this normal vector you can calculate a diffuse light with very low quality. The surfaces appears to be flat and the light seems to be calculated by face normal vectors rather than smooth vertex normal vectors. But it is possible to implement a basic light model and can be improved, by using "real" normal vectors later.



See the example. In this case the light source is assumed to be at the point of view, because the view space direction to the light (L) is vec3(0.0, 0.0, 1.0):



Vertex shader



out vec4 v_clip_pos;

main()
{
.....

gl_Positon = .....;
v_clip_pos = gl_Positon;
}


Fragment shader



#version 130

in vec4 v_clip_pos; // same as gl_Position

out vec4 FragColor;
uniform vec4 inputColor;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0); // direction to the light source
float NdotL = dot(N, L);

vec3 diffuse_color = inputColor.rgb * NdotL;
FragColor = vec4(diffuse_color.rgb, 1.0);
}


See the example which demonstrates the light model. Of course the example is implemented in WebGL and not OpenGL, but the fragment shader is very similar:






(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>








share|improve this answer


























  • looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

    – Elmi
    Nov 23 '18 at 12:17











  • @Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

    – Rabbid76
    Nov 23 '18 at 12:21












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53443845%2fdiffuse-light-with-opengl-glsl%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









4














To calculate a diffuse light or even specular hight lights, you have to know the normal vector of the surface respectively fragment. The diffuse light radiance depends on the the direction of the incident light - see How does this faking the light work on aerotwist?. Specular highlights additionally depend on the direct of view.



If you do not have normal vectors, then the surface normal vector can be calculated approximately by the partial derivative of the view space position in the fragment shader. The partial derivative can be get by the functions dFdx and dFdy.



With this normal vector you can calculate a diffuse light with very low quality. The surfaces appears to be flat and the light seems to be calculated by face normal vectors rather than smooth vertex normal vectors. But it is possible to implement a basic light model and can be improved, by using "real" normal vectors later.



See the example. In this case the light source is assumed to be at the point of view, because the view space direction to the light (L) is vec3(0.0, 0.0, 1.0):



Vertex shader



out vec4 v_clip_pos;

main()
{
.....

gl_Positon = .....;
v_clip_pos = gl_Positon;
}


Fragment shader



#version 130

in vec4 v_clip_pos; // same as gl_Position

out vec4 FragColor;
uniform vec4 inputColor;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0); // direction to the light source
float NdotL = dot(N, L);

vec3 diffuse_color = inputColor.rgb * NdotL;
FragColor = vec4(diffuse_color.rgb, 1.0);
}


See the example which demonstrates the light model. Of course the example is implemented in WebGL and not OpenGL, but the fragment shader is very similar:






(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>








share|improve this answer


























  • looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

    – Elmi
    Nov 23 '18 at 12:17











  • @Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

    – Rabbid76
    Nov 23 '18 at 12:21
















4














To calculate a diffuse light or even specular hight lights, you have to know the normal vector of the surface respectively fragment. The diffuse light radiance depends on the the direction of the incident light - see How does this faking the light work on aerotwist?. Specular highlights additionally depend on the direct of view.



If you do not have normal vectors, then the surface normal vector can be calculated approximately by the partial derivative of the view space position in the fragment shader. The partial derivative can be get by the functions dFdx and dFdy.



With this normal vector you can calculate a diffuse light with very low quality. The surfaces appears to be flat and the light seems to be calculated by face normal vectors rather than smooth vertex normal vectors. But it is possible to implement a basic light model and can be improved, by using "real" normal vectors later.



See the example. In this case the light source is assumed to be at the point of view, because the view space direction to the light (L) is vec3(0.0, 0.0, 1.0):



Vertex shader



out vec4 v_clip_pos;

main()
{
.....

gl_Positon = .....;
v_clip_pos = gl_Positon;
}


Fragment shader



#version 130

in vec4 v_clip_pos; // same as gl_Position

out vec4 FragColor;
uniform vec4 inputColor;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0); // direction to the light source
float NdotL = dot(N, L);

vec3 diffuse_color = inputColor.rgb * NdotL;
FragColor = vec4(diffuse_color.rgb, 1.0);
}


See the example which demonstrates the light model. Of course the example is implemented in WebGL and not OpenGL, but the fragment shader is very similar:






(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>








share|improve this answer


























  • looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

    – Elmi
    Nov 23 '18 at 12:17











  • @Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

    – Rabbid76
    Nov 23 '18 at 12:21














4












4








4







To calculate a diffuse light or even specular hight lights, you have to know the normal vector of the surface respectively fragment. The diffuse light radiance depends on the the direction of the incident light - see How does this faking the light work on aerotwist?. Specular highlights additionally depend on the direct of view.



If you do not have normal vectors, then the surface normal vector can be calculated approximately by the partial derivative of the view space position in the fragment shader. The partial derivative can be get by the functions dFdx and dFdy.



With this normal vector you can calculate a diffuse light with very low quality. The surfaces appears to be flat and the light seems to be calculated by face normal vectors rather than smooth vertex normal vectors. But it is possible to implement a basic light model and can be improved, by using "real" normal vectors later.



See the example. In this case the light source is assumed to be at the point of view, because the view space direction to the light (L) is vec3(0.0, 0.0, 1.0):



Vertex shader



out vec4 v_clip_pos;

main()
{
.....

gl_Positon = .....;
v_clip_pos = gl_Positon;
}


Fragment shader



#version 130

in vec4 v_clip_pos; // same as gl_Position

out vec4 FragColor;
uniform vec4 inputColor;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0); // direction to the light source
float NdotL = dot(N, L);

vec3 diffuse_color = inputColor.rgb * NdotL;
FragColor = vec4(diffuse_color.rgb, 1.0);
}


See the example which demonstrates the light model. Of course the example is implemented in WebGL and not OpenGL, but the fragment shader is very similar:






(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>








share|improve this answer















To calculate a diffuse light or even specular hight lights, you have to know the normal vector of the surface respectively fragment. The diffuse light radiance depends on the the direction of the incident light - see How does this faking the light work on aerotwist?. Specular highlights additionally depend on the direct of view.



If you do not have normal vectors, then the surface normal vector can be calculated approximately by the partial derivative of the view space position in the fragment shader. The partial derivative can be get by the functions dFdx and dFdy.



With this normal vector you can calculate a diffuse light with very low quality. The surfaces appears to be flat and the light seems to be calculated by face normal vectors rather than smooth vertex normal vectors. But it is possible to implement a basic light model and can be improved, by using "real" normal vectors later.



See the example. In this case the light source is assumed to be at the point of view, because the view space direction to the light (L) is vec3(0.0, 0.0, 1.0):



Vertex shader



out vec4 v_clip_pos;

main()
{
.....

gl_Positon = .....;
v_clip_pos = gl_Positon;
}


Fragment shader



#version 130

in vec4 v_clip_pos; // same as gl_Position

out vec4 FragColor;
uniform vec4 inputColor;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0); // direction to the light source
float NdotL = dot(N, L);

vec3 diffuse_color = inputColor.rgb * NdotL;
FragColor = vec4(diffuse_color.rgb, 1.0);
}


See the example which demonstrates the light model. Of course the example is implemented in WebGL and not OpenGL, but the fragment shader is very similar:






(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>








(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>





(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
ShProg.Use( progDraw );
ShProg.SetM44( progDraw, "u_projectionMat44", Camera.Perspective() );
ShProg.SetM44( progDraw, "u_viewMat44", Camera.LookAt() );
var modelMat = IdentM44()
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
ShProg.SetM44( progDraw, "u_modelMat44", modelMat );
ShProg.SetF3( progDraw, "color", [0.9, 0.9, 0.5] );

// 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;
var ext_standard_derivatives = gl.getExtension( "OES_standard_derivatives" ); // dFdx, dFdy
if (!ext_standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShProg.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
] );
if ( !progDraw.progObj )
return null;
progDraw.inPos = ShProg.AttrI( progDraw, "inPos" );
progDraw.inNV = ShProg.AttrI( progDraw, "inNV" );
// 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 = ;
sphere_pts.push( 0.0, 0.0, -rad_circum );
sphere_nv.push( 0.0, 0.0, -1.0 );
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 * rad_circum, r * circumY * rad_circum, h * rad_circum );
sphere_nv.push( r * circumX, r * circumY, h );
}
}
sphere_pts.push( 0.0, 0.0, rad_circum );
sphere_nv.push( 0.0, 0.0, 1.0 );
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 );
}
bufCube = VertexBuffer.Create(
[ { data : sphere_pts, attrSize : 3, attrLoc : progDraw.inPos },
{ data : sphere_nv, attrSize : 3, attrLoc : progDraw.inNV } ],
sphere_inx );

window.onresize = resize;
resize();
requestAnimationFrame(render);
}

function Fract( val ) {
return val - Math.trunc( val );
}
function CalcAng( deltaTime, intervall ) {
return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
}
function CalcMove( deltaTime, intervall, range ) {
var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
var pos = pos < 1.0 ? pos : (2.0-pos)
return range[0] + (range[1] - range[0]) * pos;
}
function EllipticalPosition( a, b, angRag ) {
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
}

glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );

function IdentM44() {
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, 1.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 = IdentM44();
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 = IdentM44();
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 ShProg = {
Create: function (shaderList) {
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) {
var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj) shaderObjs.push(shderObj);
}
var prog = {}
prog.progObj = this.Link(shaderObjs)
if (prog.progObj) {
prog.attrInx = {};
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.attrInx[name] = gl.getAttribLocation(prog.progObj, name);
}
prog.uniLoc = {};
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.uniLoc[name] = gl.getUniformLocation(prog.progObj, name);
}
}
return prog;
},
AttrI: function (prog, name) { return prog.attrInx[name]; },
UniformL: function (prog, name) { return prog.uniLoc[name]; },
Use: function (prog) { gl.useProgram(prog.progObj); },
SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); },
SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); },
SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); },
SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); },
SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); },
SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); },
SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); },
Compile: 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;
},
Link: 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(gl.getProgramInfoLog(prog));
return status ? prog : null;
} };

var VertexBuffer = {
Create: function(attribs, indices, type) {
var buffer = { buf: , attr: , inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES };
for (var i=0; i<attribs.length; ++i) {
buffer.buf.push(gl.createBuffer());
buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize });
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
if ( buffer.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
return buffer;
},
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);
}
if ( bufObj.inxLen > 0 ) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
}
else
gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of );
for (var i=0; i<bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer( gl.ARRAY_BUFFER, null );
} };

initScene();

})();

<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;

varying vec4 v_clip_pos;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;

void main()
{
vec4 pos = u_viewMat44 * u_modelMat44 * vec4( inPos, 1.0 );

v_clip_pos = u_projectionMat44 * pos;
gl_Position = v_clip_pos;
}
</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec4 v_clip_pos;

uniform highp vec3 color;

void main()
{
vec3 ndc_pos = v_clip_pos.xyz / v_clip_pos.w;
vec3 dx = dFdx( ndc_pos );
vec3 dy = dFdy( ndc_pos );

vec3 N = normalize(cross(dx, dy));
N *= sign(N.z);
vec3 L = vec3(0.0, 0.0, 1.0);
float NdotL = dot(N, L);

vec3 diffuse_color = color * NdotL;
gl_FragColor = vec4( diffuse_color.rgb, 1.0 );
}
</script>

<canvas id="canvas" style="border: none;"></canvas>






share|improve this answer














share|improve this answer



share|improve this answer








edited Dec 7 '18 at 14:33

























answered Nov 23 '18 at 12:03









Rabbid76Rabbid76

42.4k123353




42.4k123353













  • looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

    – Elmi
    Nov 23 '18 at 12:17











  • @Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

    – Rabbid76
    Nov 23 '18 at 12:21



















  • looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

    – Elmi
    Nov 23 '18 at 12:17











  • @Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

    – Rabbid76
    Nov 23 '18 at 12:21

















looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

– Elmi
Nov 23 '18 at 12:17





looks good but compiling the fragment shader code fails with a "ERROR: 0:4: '=' : cannot convert from '4-component vector of highp float' to '3-component vector of highp float'" here...

– Elmi
Nov 23 '18 at 12:17













@Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

– Rabbid76
Nov 23 '18 at 12:21





@Elmi sorry inputColor is pf type vec4, so it has to be vec3 diffuse_color = inputColor.rgb * NdotL;

– Rabbid76
Nov 23 '18 at 12:21




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53443845%2fdiffuse-light-with-opengl-glsl%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini