Since OpenGL ES 2.0 removes the fixed pipeline completely, many binding functions fails (silently) which caused me a lot of time to figure it out. Also the implementation I'm using is somewhat buggy, for example:
Array indices have to be defined in short, but the type that you tell drawElement what it is has to be unsigned short. wtf?
Another thing is that the array elements in use need to have the same length. This is not necessary in OpenGL.
Anyway, working on preliminary UIKit backend.
I'm still in no internet (ideally, practically: minimum internet) mode though, nowadays it seems to be the only way to get things done.
varying vec3 normal;
void main()
{
vec4 color;
float intensity;
vec3 n = normalize(normal);
intensity = dot(vec3(gl_LightSource[0].position),n);
if( intensity > 0.75 )
color = vec4( 1.0, 0.5, 0.5, 1.0 );
else if (intensity > 0.5)
color = vec4( 0.6, 0.3, 0.3, 1.0 );
else if (intensity > 0.25)
color = vec4( 0.4, 0.2, 0.2, 1.0 );
else
color = vec4( 0.2, 0.1, 0.1, 1.0 );
gl_FragColor = color;
}
Per vertex spot light
9.vert
void main()
{
vec3 normal, lightDir;
vec4 diffuse, ambient, globalAmbient, specular;
float NdotL, NdotHV;
normal = normalize(gl_NormalMatrix * gl_Normal);
lightDir = normalize(vec3(gl_LightSource[0].position));
NdotL = max(dot(normal, lightDir), 0.0);
diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
if (NdotL > 0.0) {
// normalize the half-vector, and then compute the
// cosine (dot product) with the normal
NdotHV = max(dot(normal, gl_LightSource[0].halfVector.xyz),0.0);
specular = gl_FrontMaterial.specular * gl_LightSource[0].specular *
pow(NdotHV,gl_FrontMaterial.shininess);
}
else {
specular = vec4( 0.0, 0.0, 0.0, 0.0);
}
/* Compute the ambient and globalAmbient terms */
ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;
gl_FrontColor = NdotL * diffuse + globalAmbient + ambient + specular;
gl_Position = ftransform();
}
9.frag
void main()
{
gl_FragColor = gl_Color;
}
Per pixel spot light
10.vert
varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;
void main()
{
/* first transform the normal into eye space and
normalize the result */
normal = normalize(gl_NormalMatrix * gl_Normal);
/* now normalize the light's direction. Note that
according to the OpenGL specification, the light
is stored in eye space. Also since we're talking about
a directional light, the position field is actually direction */
lightDir = normalize(vec3(gl_LightSource[0].position));
/* Normalize the halfVector to pass it to the fragment shader */
halfVector = normalize(gl_LightSource[0].halfVector.xyz);
/* Compute the diffuse, ambient and globalAmbient terms */
diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
gl_Position = ftransform();
}
10.frag
varying vec4 diffuse,ambient;
varying vec3 normal,lightDir,halfVector;
void main()
{
vec3 n,halfV;
float NdotL,NdotHV;
/* The ambient term will always be present */
vec4 color = ambient;
/* a fragment shader can't write a varying variable, hence we need
a new variable to store the normalized interpolated normal */
n = normalize(normal);
/* compute the dot product between normal and ldir */
NdotL = max(dot(n,lightDir),0.0);
if (NdotL > 0.0) {
color += diffuse * NdotL;
halfV = normalize(halfVector);
NdotHV = max(dot(n,halfV),0.0);
color += gl_FrontMaterial.specular *
gl_LightSource[0].specular *
pow(NdotHV, gl_FrontMaterial.shininess);
}
gl_FragColor = color;
}
Per pixel point light
11.vert
varying vec4 diffuse,ambientGlobal,ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;
void main()
{
vec4 ecPos;
vec3 aux;
normal = normalize(gl_NormalMatrix * gl_Normal);
/* these are the new lines of code to compute the light's direction */
ecPos = gl_ModelViewMatrix * gl_Vertex;
aux = vec3(gl_LightSource[0].position-ecPos);
lightDir = normalize(aux);
dist = length(aux);
halfVector = normalize(gl_LightSource[0].halfVector.xyz);
/* Compute the diffuse, ambient and globalAmbient terms */
diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
/* The ambient terms have been separated since one of them */
/* suffers attenuation */
ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
gl_Position = ftransform();
}
11.frag
varying vec4 diffuse,ambientGlobal, ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;
void main()
{
vec3 n,halfV,viewV,ldir;
float NdotL,NdotHV;
vec4 color = ambientGlobal;
float att;
/* a fragment shader can't write a varying variable, hence we need
a new variable to store the normalized interpolated normal */
n = normalize(normal);
/* compute the dot product between normal and normalized lightdir */
NdotL = max(dot(n,normalize(lightDir)),0.0);
if (NdotL > 0.0) {
att = 1.0 / (gl_LightSource[0].constantAttenuation +
gl_LightSource[0].linearAttenuation * dist +
gl_LightSource[0].quadraticAttenuation * dist * dist);
color += att * (diffuse * NdotL + ambient);
halfV = normalize(halfVector);
NdotHV = max(dot(n,halfV),0.0);
color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular *
pow(NdotHV,gl_FrontMaterial.shininess);
}
gl_FragColor = color;
}
Migrating from fixed function to programmable pipeline is a huge pain in the ass. Every chapter of the Red book is marked as deprecated or removed in OpenGL 3.1.
Anyway, I'm starting to see the benefit of programmable pipeline; Rika's object system and rendering system needs to be re-factored to take advantage of shaders, e.g. there has to be a way for models / scripts to specify / pass arguments (uniforms in GL's context) into shader scripts and the type and number of arguments have to be dynamic.
I also find a gotta of STM, i.e. threads have to be event based, otherwise TVars could be modified too often or for too long such that a consumer threads will retry forever.