2

I'm making some first steps in webgl programming. Created a simple setup following this tutorial. Managed to add a few things of my own, though stumbled with adding light, particularly - specular light. As I assume, most of it would be implemented in my fragment shader, and maybe some additions in the vertex shader and the Light module. So that's the code I provide below.

Vertex shader:

attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
varying vec3 vNormal;
varying vec2 vUv;

void main() {
    vUv = uv;
    vNormal = (model * vec4(normal, 0.)).xyz;
    gl_Position = projection * view * model * vec4(position, 1.);
}

Fragment shader:

#ifdef GL_ES
precision highp float;
#endif

uniform vec3 lightDirection;
uniform float ambientLight;
uniform sampler2D diffuse;
varying vec3 vNormal;
varying vec2 vUv;

void main() {
    float lightness = -clamp(dot(normalize(vNormal), normalize(lightDirection)), -1., 0.);
    lightness = ambientLight + (1. - ambientLight) * lightness;
    gl_FragColor = vec4(texture2D(diffuse, vUv).rgb * lightness, 1.);
}

Light.js module:

function Light () {
  this.lightDirection = new Vector3(-1, -1, -1)
  this.ambientLight = 0.3
}

Light.prototype.use = function (shaderProgram) {
  var dir = this.lightDirection
  var gl = shaderProgram.gl
  gl.uniform3f(shaderProgram.lightDirection, dir.x, dir.y, dir.z)
  gl.uniform1f(shaderProgram.ambientLight, this.ambientLight)
}

I would really appreciate your suggestions here. Thanks in advance!

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • you might enjoy [these tutorials](https://webglfundamentals.org/webgl/lessons/webgl-3d-lighting-point.html) – gman Dec 28 '18 at 02:05

1 Answers1

3

The most common and simplest light models are the Phong reflection model or the Blinn–Phong model model.

The following shader code, is based on your original code and implements the Blinn–Phong model model. In compare to your code, the light calculations are done in view space, because the specular highlight depends on the view position, which is (0, 0, 0) in view space. So the light direction has to be transformed to view space.

Vertex shader:

attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

uniform vec3 lightDirection;

varying vec3 vPos;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 lightDirectionView;

void main() 
{
    lightDirectionView = (view * vec4(lightDirection, 0.)).xyz;

    mat4 modelView = view * model;
    vec4 viewPos   = modelView * vec4(position, 1.0)
    vPos           = viewPos.xyz;
    vUv            = uv;
    vNormal        = (modelView * vec4(normal, 0.)).xyz;
    gl_Position    = projection * viewPos;
}

Fragemnt shader:

#ifdef GL_ES
precision highp float;
#endif

uniform float     shininess;
uniform float     ambientLight;
uniform sampler2D diffuse;

varying vec3 vPos;
varying vec3 vNormal;
varying vec2 vUv;
varying vec3 lightDirectionView;

void main()
{
    vec3  color     = texture2D(diffuse, vUv).rgb;

    vec3  N         = normalize( vNormal );
    vec3  L         = normalize( -lightDirectionView );
    vec3  V         = normalize( -vPos );
    vec3  H         = normalize( V + L );

    float NdotL     = dot(N, L);
    float NdotH     = dot(N, H);

    float kDiffuse  = max(0.0, NdotL);
    // float kSpecular = (shininess + 2.0) * pow(max(0.0, NdotH), shininess) / (2.0 * 3.14159265);
    float kSpecular = pow(max(0.0, NdotH), shininess);

    vec3 light_col  = color * (kDiffuse + kSpecular);
    gl_FragColor    = vec4(light_col, 1.0);
}

The value of the uniform shininess has to be a positive value in range [1, 100].

See also Phong and Gouraud Shading WebGL.

LJᛃ
  • 7,655
  • 2
  • 24
  • 35
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • @KseniaStarodubtseva You're welcome. I'm pleased to help you. – Rabbid76 Dec 30 '18 at 09:24
  • 1
    Blinn-Phong doesn't have the radiance normalization term that's being applied to `kSpecular`, also the `N` and `L` terms are flipped, conventionally `N` is the _normal_ and `L` is the _light direction_. Doing the viewspace transform of the light direction in the _fragment_ shader is rather unnecessary too as is subtracting from a zero vector (`V` term) instead of just negating. – LJᛃ Dec 30 '18 at 13:28
  • @LJᛃ `N` and `L` was a typo. – Rabbid76 Dec 30 '18 at 13:38
  • 1
    @LJᛃ `normalize( vec3(0.0) - vPos );` was meant to demonstrate that `V` is a vector between 2 points, where the view position in viewspace is (0, 0, 0). But anyway, thank you. – Rabbid76 Dec 30 '18 at 14:00
  • 1
    Hmm I see, sorry didn't mean to butcher your answer. I usually do this in world space, that said I feel like calling it _view position_ is ambiguous since to me _view position_ refers to the vertex position in view space, not the camera position. – LJᛃ Dec 30 '18 at 14:06