Vor Jahren hatte ich da mal für Swogl was angefangen, und für https://github.com/javagl/Rendering weitergemacht, und weil ich in https://github.com/javagl/JglTF gerne auch die https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common unerstützen würde, habe ich es jetzt nochmal ausgegraben:
Der Versuch, die OpenGL fixed function pipeline mit Shadern zu emulieren.
Viel von dem Shadercode hatte ich mühsam zusammengefrickelt, ausgehend von dem, was mit einem Tool generiert war, das “ShaderGen” hieß (sehr ähnlich zu aber wohl nicht genau das gleiche wie https://github.com/mojocorp/ShaderGen ).
Jetzt dachte ich, mal ein “KSKB” zu basteln, wo die beiden Methoden verglichen werden. Das ist noch nicht fertig. Fixed-Function-Texturen fehlen noch. Aber vielleicht interessiert’s ja schon jemanden.
Erstmal ein Screenshot: Links Fixed Function, rechts shader (natürlich mit Per-Fragment lighting).
[ATTACH=CONFIG]2766[/ATTACH]
import static org.lwjgl.opengl.GL11.GL_AMBIENT;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_CONSTANT_ATTENUATION;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_DIFFUSE;
import static org.lwjgl.opengl.GL11.GL_EMISSION;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_FRONT_AND_BACK;
import static org.lwjgl.opengl.GL11.GL_LIGHT0;
import static org.lwjgl.opengl.GL11.GL_LIGHTING;
import static org.lwjgl.opengl.GL11.GL_LINEAR_ATTENUATION;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_POSITION;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_QUADRATIC_ATTENUATION;
import static org.lwjgl.opengl.GL11.GL_SHININESS;
import static org.lwjgl.opengl.GL11.GL_SPECULAR;
import static org.lwjgl.opengl.GL11.GL_SPOT_CUTOFF;
import static org.lwjgl.opengl.GL11.GL_SPOT_DIRECTION;
import static org.lwjgl.opengl.GL11.GL_SPOT_EXPONENT;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glBindTexture;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glDrawElements;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glLight;
import static org.lwjgl.opengl.GL11.glLightf;
import static org.lwjgl.opengl.GL11.glLoadMatrix;
import static org.lwjgl.opengl.GL11.glMaterial;
import static org.lwjgl.opengl.GL11.glMaterialf;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glNormal3f;
import static org.lwjgl.opengl.GL11.glTexCoord2f;
import static org.lwjgl.opengl.GL11.glVertex3f;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER;
import static org.lwjgl.opengl.GL20.GL_INFO_LOG_LENGTH;
import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER;
import static org.lwjgl.opengl.GL20.glAttachShader;
import static org.lwjgl.opengl.GL20.glCompileShader;
import static org.lwjgl.opengl.GL20.glCreateProgram;
import static org.lwjgl.opengl.GL20.glCreateShader;
import static org.lwjgl.opengl.GL20.glDeleteShader;
import static org.lwjgl.opengl.GL20.glEnableVertexAttribArray;
import static org.lwjgl.opengl.GL20.glGetAttribLocation;
import static org.lwjgl.opengl.GL20.glGetProgram;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetShader;
import static org.lwjgl.opengl.GL20.glGetShaderInfoLog;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import static org.lwjgl.opengl.GL20.glShaderSource;
import static org.lwjgl.opengl.GL20.glUniform1f;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniform3;
import static org.lwjgl.opengl.GL20.glUniform4;
import static org.lwjgl.opengl.GL20.glUniformMatrix4;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.opengl.GL20.glValidateProgram;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import java.awt.Canvas;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.AWTGLCanvas;
public class SimpleRenderer
{
public static void main(String[] args)
{
System.setProperty("sun.awt.noerasebackground", "true");
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SimpleRenderer simpleRenderer = new SimpleRenderer();
f.getContentPane().add(simpleRenderer.getCanvas());
f.setSize(800,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static final int DIRECTIONAL_LIGHT_TYPE_ID = 0;
private static final int POINT_LIGHT_TYPE_ID = 1;
private static final int SPOT_LIGHT_TYPE_ID = 2;
private Canvas canvas;
private boolean initialized = false;
// The geometry data
private int indices[];
private float positions[];
private float normals[];
private float texCoords0[];
// The rendered data as a VAO and its VBOs
private int vao;
private int numIndices;
private int indicesVbo;
// The program and its attribute locations
private int program;
private int positionsAttributeLocation;
private int normalsAttributeLocation;
private int texCoords0AttributeLocation;
private int numTextures;
private int hasVertexColors;
// The matrices
private float modelMatrix[] = identity4x4();
private float normalMatrix[] = identity4x4();
private float viewMatrix[] = identity4x4();
private float projectionMatrix[] = identity4x4();
// The light data
private static class Light
{
int type;
float ambient4D[] = { 0.0f, 0.0f, 0.0f, 1.0f };
float diffuse4D[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float specular4D[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float position4D[] = { 0.0f, 0.0f, 1.0f, 0.0f };
float spotDirection3D[] = { 0.0f, 0.0f, -1.0f };
float spotExponent = 0.0f;
float spotCutoff = 90.0f;
float constantAttenuation = 1.0f;
float linearAttenuation = 0.0f;
float quadraticAttenuation = 0.0f;
}
private float globalAmbient4D[] = new float[4];
private List<Light> lights = new ArrayList<Light>();
//The material data
private static class Material
{
float ambient4D[] = { 0.2f, 0.2f, 0.2f, 1.0f };
float diffuse4D[] = { 0.8f, 0.8f, 0.8f, 1.0f };
float specular4D[] = { 0.0f, 0.0f, 0.0f, 1.0f };
float emission4D[] = { 0.0f, 0.0f, 0.0f, 1.0f };
float shininess = 0.0f;
}
private Material material;
// The matrices that are modified via mouse interaction, and
// from which the view matrix will be computed.
private float currentRotationMatrix[] = identity4x4();
private float currentTranslationMatrix[] = translation4x4(0,0,-5);
@SuppressWarnings("serial")
SimpleRenderer()
{
try
{
canvas = new AWTGLCanvas()
{
@Override
public void paintGL()
{
render();
try
{
swapBuffers();
}
catch (LWJGLException e)
{
throw new RuntimeException(
"Could not swap buffers", e);
}
}
};
MouseControl mouseControl = new MouseControl();
canvas.addMouseMotionListener(mouseControl);
canvas.addMouseWheelListener(mouseControl);
}
catch (LWJGLException e)
{
throw new RuntimeException(
"Could not create canvas", e);
}
}
private Component getCanvas()
{
return canvas;
}
private void render()
{
int width = canvas.getWidth();
int height = canvas.getHeight();
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!initialized)
{
initialize();
}
renderFixed();
renderProgrammable();
}
private void initialize()
{
initProgram();
initObject();
initLights();
initMaterial();
initialized = true;
}
private void initProgram()
{
String vertexShaderSource =
readFileAsStringUnchecked("shaders/ffShader.vs");
String fragmentShaderSource =
readFileAsStringUnchecked("shaders/ffShader.fs");
program = glCreateProgram();
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, vertexShaderSource);
glCompileShader(vertexShader);
printShaderLogInfo(vertexShader);
glAttachShader(program, vertexShader);
glDeleteShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, fragmentShaderSource);
glCompileShader(fragmentShader);
printShaderLogInfo(fragmentShader);
glAttachShader(program, fragmentShader);
glDeleteShader(fragmentShader);
glLinkProgram(program);
printProgramLogInfo(program);
glValidateProgram(program);
printProgramLogInfo(program);
glUseProgram(program);
positionsAttributeLocation =
glGetAttribLocation(program, "vertexPosition");
normalsAttributeLocation =
glGetAttribLocation(program, "vertexNormal");
texCoords0AttributeLocation =
glGetAttribLocation(program, "vertexTexCoord0");
}
private void initObject()
{
createData(2);
numIndices = indices.length;
indicesVbo = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesVbo);
IntBuffer indicesBuffer = createDirectBuffer(indices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
int positionsVbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, positionsVbo);
FloatBuffer positionsBuffer = createDirectBuffer(positions);
glBufferData(GL_ARRAY_BUFFER, positionsBuffer, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
int normalsVbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, normalsVbo);
FloatBuffer normalsBuffer = createDirectBuffer(normals);
glBufferData(GL_ARRAY_BUFFER, normalsBuffer, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
int texCoords0Vbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, texCoords0Vbo);
FloatBuffer texCoordsBuffer = createDirectBuffer(texCoords0);
glBufferData(GL_ARRAY_BUFFER, texCoordsBuffer, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
vao = glGenVertexArrays();
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, positionsVbo);
glVertexAttribPointer(
positionsAttributeLocation, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(positionsAttributeLocation);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, normalsVbo);
glVertexAttribPointer(
normalsAttributeLocation, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(normalsAttributeLocation);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, texCoords0Vbo);
glVertexAttribPointer(
texCoords0AttributeLocation, 2, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(texCoords0AttributeLocation);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
private void initLights()
{
Light light = new Light();
light.type = POINT_LIGHT_TYPE_ID;
light.ambient4D = new float[] { 0.1f, 0.1f, 0.1f, 1.0f };
light.diffuse4D = new float[] { 0.8f, 0.8f, 0.8f, 1.0f };
light.specular4D = new float[] { 0.9f, 0.9f, 0.9f, 1.0f };
light.position4D = new float[] { 3f, 3f, 3f, 1.0f };
light.constantAttenuation = 1.0f;
light.linearAttenuation = 0.0f;
light.quadraticAttenuation = 0.0f;
lights.add(light);
}
private void initMaterial()
{
material = new Material();
material.ambient4D = new float[] { 0.2f, 0.2f, 0.2f, 1.0f };
material.diffuse4D = new float[] { 1.0f, 0.0f, 0.0f, 1.0f };
material.specular4D = new float[] { 0.9f, 0.9f, 0.9f, 1.0f };
material.emission4D = new float[] { 0.1f, 0.1f, 0.1f, 1.0f };
material.shininess = 10.0f;
}
private void renderFixed()
{
int width = canvas.getWidth();
int height = canvas.getHeight();
float aspect = (float)width / height;
projectionMatrix = perspective4x4(50, aspect, 0.1f, 10000.0f);
viewMatrix = multiply4x4(
currentRotationMatrix, currentTranslationMatrix);
modelMatrix = translation4x4(-1.0f, 0.0f, 0.0f);
float[] modelViewMatrix = multiply4x4(viewMatrix, modelMatrix);
float[] invModelViewMatrix = invert4x4(modelViewMatrix);
normalMatrix = transpose4x4(invModelViewMatrix);
setMatrices();
setLights();
setMaterial();
glBegin(GL_TRIANGLES);
int n = indices.length / 3;
for (int i = 0; i < n; i++)
{
for (int j=0; j<3; j++)
{
int index = indices[i * 3 + j];
float x = positions[index * 3 + 0];
float y = positions[index * 3 + 1];
float z = positions[index * 3 + 2];
float nx = normals[index * 3 + 0];
float ny = normals[index * 3 + 1];
float nz = normals[index * 3 + 2];
float s = texCoords0[index * 2 + 0];
float t = texCoords0[index * 2 + 1];
glNormal3f(nx, ny, nz);
glTexCoord2f(s, t);
glVertex3f(x, y, z);
}
}
glEnd();
}
private void setMatrices()
{
glMatrixMode(GL_PROJECTION);
glLoadMatrix(wrapTemp(projectionMatrix));
float[] modelViewMatrix = multiply4x4(viewMatrix, modelMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrix(wrapTemp(modelViewMatrix));
}
private void setLights()
{
glEnable(GL_LIGHTING);
int numLights = lights.size();
for (int i=0; i<numLights; i++)
{
Light light = lights.get(i);
setLight(i, light);
}
}
private void setLight(int index, Light light)
{
int id = GL_LIGHT0 + index;
glEnable(id);
glLight(id, GL_AMBIENT, wrapTemp(light.ambient4D));
glLight(id, GL_DIFFUSE, wrapTemp(light.diffuse4D));
glLight(id, GL_SPECULAR, wrapTemp(light.specular4D));
glLight(id, GL_POSITION, wrapTemp(light.position4D));
glLight(id, GL_SPOT_DIRECTION, wrapTemp(
Arrays.copyOfRange(light.spotDirection3D, 0, 4)));
glLightf(id, GL_SPOT_EXPONENT, light.spotExponent);
glLightf(id, GL_SPOT_CUTOFF, light.spotCutoff);
glLightf(id, GL_CONSTANT_ATTENUATION, light.constantAttenuation);
glLightf(id, GL_LINEAR_ATTENUATION, light.linearAttenuation);
glLightf(id, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation);
}
private void setMaterial()
{
int f = GL_FRONT_AND_BACK;
glMaterial(f, GL_AMBIENT, wrapTemp(material.ambient4D));
glMaterial(f, GL_DIFFUSE, wrapTemp(material.diffuse4D));
glMaterial(f, GL_SPECULAR, wrapTemp(material.specular4D));
glMaterial(f, GL_EMISSION, wrapTemp(material.emission4D));
glMaterialf(f, GL_SHININESS, material.shininess);
}
private void renderProgrammable()
{
int width = canvas.getWidth();
int height = canvas.getHeight();
float aspect = (float)width / height;
projectionMatrix = perspective4x4(50, aspect, 0.1f, 10000.0f);
viewMatrix = multiply4x4(
currentRotationMatrix, currentTranslationMatrix);
modelMatrix = translation4x4(1.0f, 0.0f, 0.0f);
float[] modelViewMatrix = multiply4x4(viewMatrix, modelMatrix);
float[] invModelViewMatrix = invert4x4(modelViewMatrix);
normalMatrix = transpose4x4(invModelViewMatrix);
glUseProgram(program);
setUniforms();
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesVbo);
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
private void setUniforms()
{
setMatrixUniforms();
setLightUniforms();
setMaterialUniforms();
}
private void setMatrixUniforms()
{
int modelMatrixLocation =
glGetUniformLocation(program, "modelMatrix");
glUniformMatrix4(modelMatrixLocation, false,
wrapTemp(modelMatrix));
int normalMatrixLocation =
glGetUniformLocation(program, "normalMatrix");
glUniformMatrix4(normalMatrixLocation, false,
wrapTemp(normalMatrix));
int viewMatrixLocation =
glGetUniformLocation(program, "viewMatrix");
glUniformMatrix4(viewMatrixLocation, false,
wrapTemp(viewMatrix));
int projectionMatrixLocation =
glGetUniformLocation(program, "projectionMatrix");
glUniformMatrix4(projectionMatrixLocation, false,
wrapTemp(projectionMatrix));
}
private void setLightUniforms()
{
int numLights = lights.size();
int globalAmbientLocation = glGetUniformLocation(
program, "globalAmbient");
glUniform4(globalAmbientLocation,
wrapTemp(globalAmbient4D));
int numLightsLocation =
glGetUniformLocation(program, "numLights");
glUniform1i(numLightsLocation, numLights);
for (int i=0; i<numLights; i++)
{
Light light = lights.get(i);
setLightUniforms(i, light);
}
}
private void setLightUniforms(int index, Light light)
{
int lightTypeLocation = glGetUniformLocation(
program, "lights["+index+"].type");
glUniform1i(lightTypeLocation, light.type);
int lightAmbientLocation = glGetUniformLocation(
program, "lights["+index+"].ambient");
glUniform4(lightAmbientLocation,
wrapTemp(light.ambient4D));
int lightDiffuseLocation = glGetUniformLocation(
program, "lights["+index+"].diffuse");
glUniform4(lightDiffuseLocation,
wrapTemp(light.diffuse4D));
int lightSpecularLocation = glGetUniformLocation(
program, "lights["+index+"].specular");
glUniform4(lightSpecularLocation,
wrapTemp(light.specular4D));
int lightPositionLocation = glGetUniformLocation(
program, "lights["+index+"].position");
float[] lightPosition4D =
transformPoint4D(viewMatrix, light.position4D);
glUniform4(lightPositionLocation,
wrapTemp(lightPosition4D));
int lightConstantAttenuationLocation = glGetUniformLocation(
program, "lights["+index+"].constantAttenuation");
glUniform1f(lightConstantAttenuationLocation,
light.constantAttenuation);
int lightLinearAttenuationLocation = glGetUniformLocation(
program, "lights["+index+"].linearAttenuation");
glUniform1f(lightLinearAttenuationLocation,
light.linearAttenuation);
int lightQuadraticAttenuationLocation = glGetUniformLocation(
program, "lights["+index+"].quadraticAttenuation");
glUniform1f(lightQuadraticAttenuationLocation,
light.quadraticAttenuation);
int lightSpotDirectionLocation = glGetUniformLocation(
program, "lights["+index+"].spotDirection");
float[] lightSpotDirection3D =
transformPoint3D(viewMatrix, light.spotDirection3D);
glUniform3(lightSpotDirectionLocation,
wrapTemp(lightSpotDirection3D));
int lightSpotCutoffLocation = glGetUniformLocation(
program, "lights["+index+"].spotCutoff");
glUniform1f(lightSpotCutoffLocation, light.spotCutoff);
int lightSpotExponentLocation = glGetUniformLocation(
program, "lights["+index+"].spotExponent");
glUniform1f(lightSpotExponentLocation, light.spotExponent);
}
private void setMaterialUniforms()
{
int materialAmbientLocation = glGetUniformLocation(
program, "material.ambient");
glUniform4(materialAmbientLocation,
wrapTemp(material.ambient4D));
int materialDiffuseLocation = glGetUniformLocation(
program, "material.diffuse");
glUniform4(materialDiffuseLocation,
wrapTemp(material.diffuse4D));
int materialSpecularLocation = glGetUniformLocation(
program, "material.specular");
glUniform4(materialSpecularLocation,
wrapTemp(material.specular4D));
int materialEmissionLocation = glGetUniformLocation(
program, "material.emission");
glUniform4(materialEmissionLocation,
wrapTemp(material.emission4D));
int materialShininessLocation = glGetUniformLocation(
program, "material.shininess");
glUniform1f(materialShininessLocation, material.shininess);
}
//==========================================================================
// Utility class for the mouse interaction
private class MouseControl
implements MouseMotionListener, MouseWheelListener
{
private Point previousMousePosition = new Point();
@Override
public void mouseDragged(MouseEvent e)
{
int dx = e.getX() - previousMousePosition.x;
int dy = e.getY() - previousMousePosition.y;
if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) ==
MouseEvent.BUTTON3_DOWN_MASK)
{
currentTranslationMatrix = multiply4x4(
translation4x4(dx / 150.0f, -dy / 150.0f, 0),
currentTranslationMatrix);
}
else if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) ==
MouseEvent.BUTTON1_DOWN_MASK)
{
currentRotationMatrix = multiply4x4(
currentRotationMatrix, rotationX4x4(dy));
currentRotationMatrix = multiply4x4(
currentRotationMatrix, rotationY4x4(dx));
}
previousMousePosition = e.getPoint();
canvas.repaint();
}
@Override
public void mouseMoved(MouseEvent e)
{
previousMousePosition = e.getPoint();
}
@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
currentTranslationMatrix = multiply4x4(
translation4x4(0, 0, e.getWheelRotation() * 0.25f),
currentTranslationMatrix);
previousMousePosition = e.getPoint();
canvas.repaint();
}
}
//==========================================================================
// IO utility methods
private static String readFileAsStringUnchecked(String fileName)
{
try
{
return readFileAsString(fileName);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
private static String readFileAsString(String fileName) throws IOException
{
try (FileInputStream inputStream = new FileInputStream(fileName))
{
return readStreamAsString(inputStream);
}
}
private static String readStreamAsString(InputStream inputStream)
throws IOException
{
try (BufferedReader br =
new BufferedReader(new InputStreamReader(inputStream)))
{
return br.lines().collect(Collectors.joining("
"));
}
}
//==========================================================================
// Buffer utility methods
private static ByteBuffer createByteBuffer(int size)
{
return ByteBuffer.allocateDirect(size).
order(ByteOrder.nativeOrder());
}
private static FloatBuffer createFloatBuffer(int size)
{
return createByteBuffer(size * 4).asFloatBuffer();
}
private static IntBuffer createIntBuffer(int size)
{
return createByteBuffer(size * 4).asIntBuffer();
}
private static IntBuffer createDirectBuffer(int a[])
{
IntBuffer b = createIntBuffer(a.length);
b.put(a);
b.rewind();
return b;
}
private static FloatBuffer createDirectBuffer(float a[])
{
FloatBuffer b = createFloatBuffer(a.length);
b.put(a);
b.rewind();
return b;
}
private static FloatBuffer tempFloatBuffer = null;
private static FloatBuffer wrapTemp(float array[])
{
if (tempFloatBuffer == null ||
tempFloatBuffer.capacity() < array.length)
{
tempFloatBuffer = ByteBuffer
.allocateDirect(array.length * Float.BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
}
tempFloatBuffer.position(0);
tempFloatBuffer.limit(tempFloatBuffer.capacity());
tempFloatBuffer.put(array);
tempFloatBuffer.flip();
return tempFloatBuffer;
}
//==========================================================================
// Matrix utility methods
private static float[] perspective4x4(
float fovyDeg, float aspect, float zNear, float zFar)
{
// Adapted from The Mesa 3-D graphics library.
// Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
// Published under the MIT license (see the header of this file)
float radians = (float)Math.toRadians(fovyDeg / 2);
float deltaZ = zFar - zNear;
float sine = (float)Math.sin(radians);
if ((deltaZ == 0) || (sine == 0) || (aspect == 0))
{
return identity4x4();
}
float cotangent = (float)Math.cos(radians) / sine;
float m[] = identity4x4();
m[0] = cotangent / aspect;
m[5] = cotangent;
m[10] = -(zFar + zNear) / deltaZ;
m[11] = -1;
m[14] = -2 * zNear * zFar / deltaZ;
m[15] = 0.0f;
return m;
}
private static float[] identity4x4()
{
float m[] = new float[16];
m[0] = 1.0f;
m[5] = 1.0f;
m[10] = 1.0f;
m[15] = 1.0f;
return m;
}
private static float[] multiply4x4(float m0[], float m1[])
{
float m[] = new float[16];
for (int x = 0; x < 16; x += 4)
{
for (int y = 0; y < 4; y++)
{
m[x+y] =
m0[x+0] * m1[y+ 0] +
m0[x+1] * m1[y+ 4] +
m0[x+2] * m1[y+ 8] +
m0[x+3] * m1[y+12];
}
}
return m;
}
private static float[] translation4x4(float x, float y, float z)
{
float m[] = identity4x4();
m[12] = x;
m[13] = y;
m[14] = z;
return m;
}
private static float[] rotationX4x4(float angleDeg)
{
float m[] = identity4x4();
float angleRad = (float)Math.toRadians(angleDeg);
float ca = (float)Math.cos(angleRad);
float sa = (float)Math.sin(angleRad);
m[ 5] = ca;
m[ 6] = sa;
m[ 9] = -sa;
m[10] = ca;
return m;
}
private static float[] rotationY4x4(float angleDeg)
{
float m[] = identity4x4();
float angleRad = (float)Math.toRadians(angleDeg);
float ca = (float)Math.cos(angleRad);
float sa = (float)Math.sin(angleRad);
m[ 0] = ca;
m[ 2] = -sa;
m[ 8] = sa;
m[10] = ca;
return m;
}
static float[] invert4x4(float m[])
{
// Adapted from The Mesa 3-D graphics library.
// Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
// Published under the MIT license (see the header of this file)
float FLOAT_EPSILON = 1e-6f;
float m0 = m[ 0];
float m1 = m[ 1];
float m2 = m[ 2];
float m3 = m[ 3];
float m4 = m[ 4];
float m5 = m[ 5];
float m6 = m[ 6];
float m7 = m[ 7];
float m8 = m[ 8];
float m9 = m[ 9];
float mA = m[10];
float mB = m[11];
float mC = m[12];
float mD = m[13];
float mE = m[14];
float mF = m[15];
float inv[] = new float[4*4];
inv[ 0] = m5 * mA * mF - m5 * mB * mE - m9 * m6 * mF +
m9 * m7 * mE + mD * m6 * mB - mD * m7 * mA;
inv[ 4] = -m4 * mA * mF + m4 * mB * mE + m8 * m6 * mF -
m8 * m7 * mE - mC * m6 * mB + mC * m7 * mA;
inv[ 8] = m4 * m9 * mF - m4 * mB * mD - m8 * m5 * mF +
m8 * m7 * mD + mC * m5 * mB - mC * m7 * m9;
inv[12] = -m4 * m9 * mE + m4 * mA * mD + m8 * m5 * mE -
m8 * m6 * mD - mC * m5 * mA + mC * m6 * m9;
inv[ 1] = -m1 * mA * mF + m1 * mB * mE + m9 * m2 * mF -
m9 * m3 * mE - mD * m2 * mB + mD * m3 * mA;
inv[ 5] = m0 * mA * mF - m0 * mB * mE - m8 * m2 * mF +
m8 * m3 * mE + mC * m2 * mB - mC * m3 * mA;
inv[ 9] = -m0 * m9 * mF + m0 * mB * mD + m8 * m1 * mF -
m8 * m3 * mD - mC * m1 * mB + mC * m3 * m9;
inv[13] = m0 * m9 * mE - m0 * mA * mD - m8 * m1 * mE +
m8 * m2 * mD + mC * m1 * mA - mC * m2 * m9;
inv[ 2] = m1 * m6 * mF - m1 * m7 * mE - m5 * m2 * mF +
m5 * m3 * mE + mD * m2 * m7 - mD * m3 * m6;
inv[ 6] = -m0 * m6 * mF + m0 * m7 * mE + m4 * m2 * mF -
m4 * m3 * mE - mC * m2 * m7 + mC * m3 * m6;
inv[10] = m0 * m5 * mF - m0 * m7 * mD - m4 * m1 * mF +
m4 * m3 * mD + mC * m1 * m7 - mC * m3 * m5;
inv[14] = -m0 * m5 * mE + m0 * m6 * mD + m4 * m1 * mE -
m4 * m2 * mD - mC * m1 * m6 + mC * m2 * m5;
inv[ 3] = -m1 * m6 * mB + m1 * m7 * mA + m5 * m2 * mB -
m5 * m3 * mA - m9 * m2 * m7 + m9 * m3 * m6;
inv[ 7] = m0 * m6 * mB - m0 * m7 * mA - m4 * m2 * mB +
m4 * m3 * mA + m8 * m2 * m7 - m8 * m3 * m6;
inv[11] = -m0 * m5 * mB + m0 * m7 * m9 + m4 * m1 * mB -
m4 * m3 * m9 - m8 * m1 * m7 + m8 * m3 * m5;
inv[15] = m0 * m5 * mA - m0 * m6 * m9 - m4 * m1 * mA +
m4 * m2 * m9 + m8 * m1 * m6 - m8 * m2 * m5;
// (Ain't that pretty?)
float det = m0 * inv[0] + m1 * inv[4] + m2 * inv[8] + m3 * inv[12];
if (Math.abs(det) <= FLOAT_EPSILON)
{
return identity4x4();
}
float invDet = 1.0f / det;
for (int i = 0; i < 16; i++)
{
inv** *= invDet;
}
return inv;
}
static float[] transpose4x4(float m[])
{
float m0 = m[ 0];
float m1 = m[ 1];
float m2 = m[ 2];
float m3 = m[ 3];
float m4 = m[ 4];
float m5 = m[ 5];
float m6 = m[ 6];
float m7 = m[ 7];
float m8 = m[ 8];
float m9 = m[ 9];
float mA = m[10];
float mB = m[11];
float mC = m[12];
float mD = m[13];
float mE = m[14];
float mF = m[15];
float t[] = new float[4*4];
t[ 0] = m0;
t[ 1] = m4;
t[ 2] = m8;
t[ 3] = mC;
t[ 4] = m1;
t[ 5] = m5;
t[ 6] = m9;
t[ 7] = mD;
t[ 8] = m2;
t[ 9] = m6;
t[10] = mA;
t[11] = mE;
t[12] = m3;
t[13] = m7;
t[14] = mB;
t[15] = mF;
return t;
}
private static float[] transformPoint3D(
float matrix4x4[], float point3D[])
{
float result3D[] = new float[3];
for (int r=0; r<3; r++)
{
for (int c=0; c<3; c++)
{
int index = c * 4 + r;
float m = matrix4x4[index];
result3D[r] += m * point3D```;
}
int index = 3 * 4 + r;
float m = matrix4x4[index];
result3D[r] += m;
}
return result3D;
}
private static float[] transformPoint4D(
float matrix4x4[], float point4D[])
{
float result4D[] = new float[4];
for (int r=0; r<3; r++)
{
for (int c=0; c<4; c++)
{
int index = c * 4 + r;
float m = matrix4x4[index];
result4D[r] += m * point4D```;
}
}
return result4D;
}
//==========================================================================
// Utility methods
private void printShaderLogInfo(int id)
{
IntBuffer infoLogLength = ByteBuffer.allocateDirect(4)
.order(ByteOrder.nativeOrder()).asIntBuffer();
glGetShader(id, GL_INFO_LOG_LENGTH, infoLogLength);
if (infoLogLength.get(0) > 0)
{
infoLogLength.put(0, infoLogLength.get(0)-1);
}
ByteBuffer infoLog = ByteBuffer.allocateDirect(infoLogLength.get(0))
.order(ByteOrder.nativeOrder());
glGetShaderInfoLog(id, infoLogLength, infoLog);
String infoLogString =
Charset.forName("US-ASCII").decode(infoLog).toString();
if (infoLogString.trim().length() > 0)
{
System.out.println("shader log:
"+infoLogString);
}
}
private void printProgramLogInfo(int id)
{
IntBuffer infoLogLength = ByteBuffer.allocateDirect(4)
.order(ByteOrder.nativeOrder()).asIntBuffer();
glGetProgram(id, GL_INFO_LOG_LENGTH, infoLogLength);
if (infoLogLength.get(0) > 0)
{
infoLogLength.put(0, infoLogLength.get(0)-1);
}
ByteBuffer infoLog = ByteBuffer.allocateDirect(infoLogLength.get(0))
.order(ByteOrder.nativeOrder());
glGetProgramInfoLog(id, infoLogLength, infoLog);
String infoLogString =
Charset.forName("US-ASCII").decode(infoLog).toString();
if (infoLogString.trim().length() > 0)
{
System.out.println("program log:
"+infoLogString);
}
}
//==========================================================================
// Data
private void createData(int depth)
{
// Coordinates and indices taken from the redbook
final float X = 0.525731112119133606f;
final float Z = 0.850650808352039932f;
float vdata[][] =
{
{ -X, 0.0f, Z},
{ X, 0.0f, Z},
{ -X, 0.0f, -Z},
{ X, 0.0f, -Z},
{0.0f, Z, X},
{0.0f, Z, -X},
{0.0f, -Z, X},
{0.0f, -Z, -X},
{ Z, X, 0.0f},
{ -Z, X, 0.0f},
{ Z, -X, 0.0f},
{ -Z, -X, 0.0f}
};
int tindices[][] =
{
{ 0, 4, 1},
{ 0, 9, 4},
{ 9, 5, 4},
{ 4, 5, 8},
{ 4, 8, 1},
{ 8,10, 1},
{ 8, 3,10},
{ 5, 3, 8},
{ 5, 2, 3},
{ 2, 7, 3},
{ 7,10, 3},
{ 7, 6,10},
{ 7,11, 6},
{11, 0, 6},
{ 0, 1, 6},
{ 6, 1,10},
{ 9, 0,11},
{ 9,11, 2},
{ 9, 2, 5},
{ 7, 2,11}
};
int power = (int) Math.round(Math.pow(4, depth));
int numTriangles = 20 * power;
indices = new int[numTriangles * 3];
IntBuffer indicesBuffer = IntBuffer.wrap(indices);
int numVertices = 60 * power;
positions = new float[numVertices * 3];
FloatBuffer positionsBuffer = FloatBuffer.wrap(positions);
for (int i = 0; i < 20; i++)
{
FloatBuffer v1 = FloatBuffer.wrap(vdata[tindices**[0]]);
FloatBuffer v2 = FloatBuffer.wrap(vdata[tindices**[1]]);
FloatBuffer v3 = FloatBuffer.wrap(vdata[tindices**[2]]);
subdivide(v1, v2, v3, positionsBuffer, indicesBuffer, depth);
}
normals = positions.clone();
texCoords0 = new float[numVertices * 2];
for (int i = 0; i < numVertices; i++)
{
float nx = positionsBuffer.get(i * 3 + 0);
float ny = positionsBuffer.get(i * 3 + 1);
float nz = positionsBuffer.get(i * 3 + 2);
texCoords0[i * 2 + 0] =
(float) (Math.atan2(nx, nz) / (2 * Math.PI) + 0.5);
texCoords0[i * 2 + 1] = (float) (Math.asin(ny) / Math.PI + 0.5);
}
}
// Subdivision as described in the redbook
static void subdivide(
FloatBuffer v1, FloatBuffer v2, FloatBuffer v3,
FloatBuffer vertices, IntBuffer indices, long depth)
{
if (depth == 0)
{
drawTriangle(v1, v2, v3, vertices, indices);
return;
}
FloatBuffer v12 = FloatBuffer.allocate(3);
FloatBuffer v23 = FloatBuffer.allocate(3);
FloatBuffer v31 = FloatBuffer.allocate(3);
for (int i = 0; i < 3; i++)
{
v12.put(i, v1.get(i) + v2.get(i));
v23.put(i, v2.get(i) + v3.get(i));
v31.put(i, v3.get(i) + v1.get(i));
}
normalize3D(v12);
normalize3D(v23);
normalize3D(v31);
subdivide( v1, v12, v31, vertices, indices, depth-1);
subdivide( v2, v23, v12, vertices, indices, depth-1);
subdivide( v3, v31, v23, vertices, indices, depth-1);
subdivide(v12, v23, v31, vertices, indices, depth-1);
}
private static void normalize3D(FloatBuffer v)
{
float x = v.get(0);
float y = v.get(1);
float z = v.get(2);
float length = (float) Math.sqrt(x * x + y * y + z * z);
float invLength = 1.0f / length;
v.put(0, x * invLength);
v.put(1, y * invLength);
v.put(2, z * invLength);
}
private static void drawTriangle(
FloatBuffer v1, FloatBuffer v2, FloatBuffer v3,
FloatBuffer vertices, IntBuffer indices)
{
indices.put(vertices.position() / 3);
for (int i = 0; i < 3; i++)
{
vertices.put(v1.get(i));
}
indices.put(vertices.position() / 3);
for (int i = 0; i < 3; i++)
{
vertices.put(v3.get(i));
}
indices.put(vertices.position() / 3);
for (int i = 0; i < 3; i++)
{
vertices.put(v2.get(i));
}
}
}
Vertex shader:
#version 330 core
#define MAX_LIGHTS 8
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_POINT 1
#define LIGHT_TYPE_SPOT 2
// The attributes of the object, stored in VBOs.
// Namely the position, normal, color and texcoords
in vec3 vertexPosition;
in vec3 vertexNormal;
in vec4 vertexColor;
in vec2 vertexTexcoord0;
in vec2 vertexTexcoord1;
in vec2 vertexTexcoord2;
in vec2 vertexTexcoord3;
uniform int numTextures;
uniform int hasColors;
//The matrices
uniform mat4 modelMatrix;
uniform mat4 normalMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
//The light structure. The position and spotDirection
//are given in view space
struct Light
{
int type;
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
vec3 spotDirection;
float spotExponent;
float spotCutoff;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
// The lights
uniform Light lights[MAX_LIGHTS];
uniform int numLights;
uniform vec4 globalAmbient;
//The material structure
struct Material
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 emission;
float shininess;
};
// The material
uniform Material material;
// Position, normal, color and texture coordinates for fragment shader
out vec3 fragmentPosition;
out vec3 fragmentNormal;
out vec4 fragmentColor;
out vec2 fragmentTexcoord0;
out vec2 fragmentTexcoord1;
out vec2 fragmentTexcoord2;
out vec2 fragmentTexcoord3;
void main (void)
{
// Compute the eye-coordinate position of the vertex
mat4 modelviewMatrix = viewMatrix*modelMatrix;
vec4 position = vec4(vertexPosition, 1.0);
vec4 fragmentPosition4 = modelviewMatrix * position;
fragmentPosition = fragmentPosition4.xyz / fragmentPosition4.w;
// Compute the transformed normal
fragmentNormal = (normalMatrix*vec4(normalize(vertexNormal), 0.0)).xyz;
fragmentColor = vertexColor;
// Pass the texture coordinates to the fragment shader
if (numTextures > 0) fragmentTexcoord0 = vertexTexcoord0;
if (numTextures > 1) fragmentTexcoord1 = vertexTexcoord1;
if (numTextures > 2) fragmentTexcoord2 = vertexTexcoord2;
if (numTextures > 3) fragmentTexcoord3 = vertexTexcoord3;
// Do fixed functionality vertex transform
gl_Position = projectionMatrix*modelviewMatrix*position;
}
Fragment shader:
#version 330 core
#define MAX_LIGHTS 8
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_POINT 1
#define LIGHT_TYPE_SPOT 2
// The position, normal, color and texture coordinates
in vec3 fragmentPosition;
in vec3 fragmentNormal;
in vec4 fragmentColor;
in vec2 fragmentTexcoord0;
in vec2 fragmentTexcoord1;
in vec2 fragmentTexcoord2;
in vec2 fragmentTexcoord3;
uniform int numTextures;
uniform int hasColors;
//The matrices
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 normalMatrix;
//The light structure. The position and spotDirection
//are given in view space
struct Light
{
int type;
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
vec3 spotDirection;
float spotExponent;
float spotCutoff;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
// The lights
uniform Light lights[MAX_LIGHTS];
uniform int numLights;
uniform vec4 globalAmbient;
//The material structure
struct Material
{
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 emission;
float shininess;
};
// The material
uniform Material material;
// The texture samplers
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform sampler2D texture3;
// The ambient, diffuse and specular
// components of the lights output
vec4 lightsAmbient;
vec4 lightsDiffuse;
vec4 lightsSpecular;
vec4 lightsColor;
// The resulting pixel color
out vec4 outColor;
//Compute the contribution of the point light with index i
//to lightsAmbient, lightsDiffuse and lightsSpecular
void pointLight(in int i)
{
// The eye position in view space is constant
vec3 eye = vec3 (0.0, 0.0, 1.0);
// Normalize the input normal
vec3 normal = normalize(fragmentNormal);
// Compute vector from surface to light position
vec3 VP = vec3 (lights**.position) - fragmentPosition;
// Compute distance between surface and light position
float d = length(VP);
// Normalize the vector from surface to light position
VP = normalize(VP);
// Compute attenuation
float attenuation = 1.0 /
(lights**.constantAttenuation +
lights**.linearAttenuation * d +
lights**.quadraticAttenuation * d * d);
// Compute the ambient contribution
lightsAmbient += lights**.ambient * attenuation;
// Check if the surface faces the light
float nDotVP = dot(normal, VP);
if (!gl_FrontFacing)
{
nDotVP = -nDotVP;
}
if (nDotVP > 0)
{
// Add the diffuse contribution
lightsDiffuse += lights**.diffuse * nDotVP * attenuation;
// Compute the specular contribution
vec3 halfVector = normalize(VP + eye);
float nDotHV = dot(normal, halfVector);
if (!gl_FrontFacing)
{
nDotHV = -nDotHV;
}
float pf = pow(nDotHV, material.shininess);
lightsSpecular += lights**.specular * pf * attenuation;
}
}
//Compute the contribution of the spot light with index i
//to lightsAmbient, lightsDiffuse and lightsSpecular
void spotLight(in int i)
{
// The eye position in view space is constant
vec3 eye = vec3 (0.0, 0.0, 1.0);
// Normalize the input normal
vec3 normal = normalize(fragmentNormal);
// Compute vector from surface to light position
vec3 VP = vec3 (lights**.position) - fragmentPosition;
// Compute distance between surface and light position
float d = length(VP);
// Normalize the vector from surface to light position
VP = normalize(VP);
// Compute attenuation
float attenuation = 1.0 /
(lights**.constantAttenuation +
lights**.linearAttenuation * d +
lights**.quadraticAttenuation * d * d);
// Check if the surface point is inside the light cone
float spotDot = dot(-VP, normalize(lights**.spotDirection));
float spotAttenuation = 0;
if (spotDot >= cos(radians(lights**.spotCutoff)))
{
spotAttenuation = pow(spotDot, lights**.spotExponent);
}
attenuation *= spotAttenuation;
// Compute the ambient contribution
lightsAmbient += lights**.ambient * attenuation;
// Check if the surface faces the light
float nDotVP = dot(normal, VP);
if (!gl_FrontFacing)
{
nDotVP = -nDotVP;
}
if (nDotVP > 0)
{
// Add the diffuse contribution
lightsDiffuse += lights**.diffuse * nDotVP * attenuation;
// Compute the specular contribution
vec3 halfVector = normalize(VP + eye);
float nDotHV = dot(normal, halfVector);
if (!gl_FrontFacing)
{
nDotHV = -nDotHV;
}
float pf = pow(nDotHV, material.shininess);
lightsSpecular += lights**.specular * pf * attenuation;
}
}
//Compute the contribution of the directional light with index i
//to lightsAmbient, lightsDiffuse and lightsSpecular
void directionalLight(in int i)
{
// The eye position in view space is constant
vec3 eye = vec3 (0.0, 0.0, 1.0);
// Normalize the input normal
vec3 normal = normalize(fragmentNormal);
// Compute the light direction (stored in the position
// for directional lights)
vec3 VP = normalize(lights**.position.xyz);
// Compute the ambient contribution
lightsAmbient += lights**.ambient;
// Check if the surface faces the light
float nDotVP = dot(normal, VP);
if (!gl_FrontFacing)
{
nDotVP = -nDotVP;
}
if (nDotVP > 0)
{
// Add the diffuse contribution
lightsDiffuse += lights**.diffuse * nDotVP;
// Compute the specular contribution
vec3 halfVector = normalize(VP + eye);
float nDotHV = dot(normal, halfVector);
if (!gl_FrontFacing)
{
nDotHV = -nDotHV;
}
float pf = pow(nDotHV, material.shininess);
lightsSpecular += lights**.specular * pf;
}
}
// Compute the contributions of all lights
// to lightsAmbient, lightsDiffuse and lightsSpecular
void handleLights()
{
// Initialize the light intensity accumulators
lightsAmbient = globalAmbient;
lightsDiffuse = vec4 (0.0);
lightsSpecular = vec4 (0.0);
// Accumulate all light contributions
for (int i=0; i<numLights; i++)
{
Light light = lights**;
if (light.type == LIGHT_TYPE_DIRECTIONAL)
{
directionalLight(i);
}
if (light.type == LIGHT_TYPE_POINT)
{
pointLight(i);
}
if (light.type == LIGHT_TYPE_SPOT)
{
spotLight(i);
}
}
// Light component of the pixel color
lightsColor =
lightsAmbient * material.ambient +
lightsDiffuse * material.diffuse +
lightsSpecular * material.specular +
material.emission;
lightsColor = clamp(lightsColor, 0.0, 1.0 );
}
void main (void)
{
vec4 color = vec4(1.0);
if (hasColors != 0)
{
fragmentColor;
}
if (numTextures == 1)
{
color = texture2D(texture0, fragmentTexcoord0);
}
if (numTextures == 2)
{
color = texture2D(texture0, fragmentTexcoord0);
color = mix(color, texture2D(texture1, fragmentTexcoord1), 0.5);
}
if (numTextures == 3)
{
color = texture2D(texture0, fragmentTexcoord0);
color = mix(color, texture2D(texture1, fragmentTexcoord1), 0.5);
color = mix(color, texture2D(texture2, fragmentTexcoord2), 0.5);
}
if (numTextures == 4)
{
color = texture2D(texture0, fragmentTexcoord0);
color = mix(color, texture2D(texture1, fragmentTexcoord1), 0.5);
color = mix(color, texture2D(texture2, fragmentTexcoord2), 0.5);
color = mix(color, texture2D(texture3, fragmentTexcoord3), 0.5);
}
if (numLights == 0)
{
outColor = color;
}
else
{
handleLights();
outColor = lightsColor * color;
}
}