A shader in the field of computer graphics is a set of software instructions, which is used primarily to calculate rendering effects on graphics hardware with a high degree of flexibility.
We will define a Vertex and a Fragment shader to create a smooth transition of light on a sphere.
Most of the work will happen in the Vertex Shader (gouraud.vert, you can find the content of the file down below).
Our main application written in C++ will only create the sphere and load the shaders for us while the real magic
happens in the shader files themselves. They are written
in the OpenGL Shading Language.
Since the syntax is very similar to C we won't have any problems reading or writing the code!
Diffuse light is light that is reflected from the surface of the object in all directions equally. The position of the viewer (camera/eye) doesn't matter here. To calculate diffuse light we create the dot product between light_vert and normal_camera. To avoid negative values we use the function max().
Specular light is light that is reflected from the surface of the object along the reflection vector light_refl. This also means that it looks different depending on the position of the viewer (camera/eye). The closer the viewer is to the reflection vector the stronger the light appears. The dot product between light_refl and vertex_position_camera calculates the specular light.
const vec4 light_position_world = vec4(4.0, 4.0, 4.0, 1.0);
varying float specular_intensity;
varying float diffuse_intensity;
void main(void) {
vec4 vertex_position_camera = gl_ModelViewMatrix * gl_Vertex;
vec3 normal_camera = normalize(gl_NormalMatrix * gl_Normal);
vec4 light_position_camera = gl_ModelViewMatrix * light_position_world;
vec3 light_vert = normalize(vec3(light_position_camera - vertex_position_camera));
vec3 light_refl = normalize(reflect(light_vert, normal_camera));
// diffuse light
diffuse_intensity = max(dot(light_vert, normal_camera), 0.0);
// specular light
specular_intensity = max(dot(light_refl, normalize(vec3(vertex_position_camera))), 0.0);
specular_intensity = pow(specular_intensity, 6.0);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
const vec4 diffuse_color = vec4(1.0, 0.0, 0.0, 1.0);
const vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);
varying float specular_intensity;
varying float diffuse_intensity;
void main(void) {
gl_FragColor =
diffuse_color * diffuse_intensity +
specular_color * specular_intensity;
}
This simple application will create a sphere and load our shader onto it
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/StateSet>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <iostream>
using namespace osg;
Node *startup() {
Group *scene = new Group();
Geode *sphere = new Geode();
sphere->addDrawable(new ShapeDrawable(new Sphere(Vec3(), 1)));
StateSet *sphereStateSet = sphere->getOrCreateStateSet();
sphereStateSet->ref();
Program *programObject = new Program();
Shader *vertexObject = new Shader(Shader::VERTEX);
Shader *fragmentObject = new Shader(Shader::FRAGMENT);
programObject->addShader(fragmentObject);
programObject->addShader(vertexObject);
vertexObject->loadShaderSourceFromFile("gouraud.vert");
fragmentObject->loadShaderSourceFromFile("gouraud.frag");
sphereStateSet->setAttributeAndModes(programObject, StateAttribute::ON);
scene->addChild(sphere);
return scene;
}
int main() {
Node *scene = startup();
if (!scene) return 1;
osgViewer::Viewer viewer;
viewer.setSceneData(scene);
viewer.setCameraManipulator(new osgGA::TrackballManipulator());
viewer.realize();
while (!viewer.done()) {
viewer.frame();
}
}