i tried to implement a simple raytracing algorithm in an openGl fragment shader which draws a single sphere.
sometimes it draws a distorted sphere, but most of the times it draws nothing.
with the actual sphere origin and radius i get a slightly destored sphere in the right upper corner.
#version 430 core
uniform ivec2 viewportDimensions;
uniform mat4 gl_ProjectionMatrix;
uniform ivec4 viewport;
out vec3 color;
struct Ray {
vec3 origin;
vec3 direction;
};
struct Sphere {
vec3 origin;
float radius;
};
float zNear = -0.1f;
float zFar = -100.0f;
float fieldOfViewX = 3.1415926535897932384626433832795 / 2.0f;
float sampleRay(Ray ray,Sphere s, float distance);
Ray computeEyeRay(float, float, int, int);
bool intersect(Ray r, Sphere s);
bool solveQuadratic(float a, float b, float c);
Ray calcEyeFromWindow(vec3 windowSpace);
void main() {
// Ray r = computeEyeRay(gl_FragCoord.x + 0.5f, gl_FragCoord.y + 0.5f, viewportDimensions.x, viewportDimensions.y);
Ray r = calcEyeFromWindow(vec3(gl_FragCoord.x + 0.5f, gl_FragCoord.y + 0.5f, 1f));
Sphere s;
s.origin = vec3(0.5f,-0.5f,-0.7f);
s.radius = 0.8f;
if (intersect(r,s))
color = vec3(1,1,1);
else
color = vec3(0,0,0);
}
Ray calcEyeFromWindow(vec3 windowSpace)
{
vec4 ndcPos;
ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /
(gl_DepthRange.far - gl_DepthRange.near);
ndcPos.w = 1.0;
vec4 clipPos = ndcPos / gl_FragCoord.w;
vec4 eyePos = inverse(gl_ProjectionMatrix) * clipPos;
Ray r;
r.origin = eyePos.xyz;
r.direction =normalize(eyePos.xyz);
return r;
}
Ray computeEyeRay(float x, float y, int width, int height) {
const float aspect = float(height) / float(width);
const float s = -2.0f * tan(fieldOfViewX * 0.5f);
const vec3 start = vec3( (float(x) / float(width) - 0.5) * s,
-(float(y) / float(height) - 0.5f) * s * aspect,
1.0f) * zNear;
float startLength = sqrt( (start.x * start.x) + (start.y * start.y) + (start.z * start.z) );
Ray e;
e.origin = start;
e.direction = normalize(start);
return e;
}
bool intersect(Ray r, Sphere s) {
float a = dot(r.direction,r.direction);
float b = dot(r.direction, 2.0 * (r.origin-s.origin));
float c = dot(s.origin, s.origin) + dot(r.origin,r.origin) +-2.0*dot(r.origin,s.origin) - (s.radius*s.radius);
float disc = b*b + (-4.0)*a*c;
if (disc < 0)
return false;
return true;
}
bool solveQuadratic(float a, float b, float c) {
float t, t_1;
float disc = a * b - 4 * a * c;
if (disc < 0)
return false;
else {
if (disc == 0)
t = -0.5 * b / a;
else {
float q = (b > 0) ? -0.5 * (b+sqrt(disc)) : -0.5 * (b-sqrt(disc));
t = q / a;
t_1 = c / q;
}
}
return true;
}
here is the rest of the program if anyone wants to compile and test it.
#include <stdio.h>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <glm\glm.hpp>
// include opengl headers
#include <GL/glew.h>
#include <GL/freeglut.h>
const int VIEWPORT_DIMENSION_X = 1280;
const int VIEWPORT_DIMENSION_Y = 1280;
void glutInitialization(int argc, char **argv);
void glewInitialization();
void display();
void resize(int w, int h);
void idle();
void drawSphere(float x, float y, float z);
GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path);
GLuint VertexArrayID;
GLuint vertexbuffer; // This will identify our vertex buffer
GLuint programID;
static const GLfloat quadvertices[] = {
-1.0f, 1.0f, .0f,
-1.0f, -1.0f, .0f,
1.0f, -1.0f, .0f,
1.0f, -1.0f, .0f,
1.0f, 1.0f, .0f,
-1.0f, 1.0f, .0f
};
static const GLfloat g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
int main(int argc, char **argv)
{
glutInitialization(argc, argv);
glewInitialization();
// create VAO
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
// Generate 1 buffer
glGenBuffers(1, &vertexbuffer);
// Give our vertices to OpenGL.
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadvertices), quadvertices, GL_STATIC_DRAW);
glVertexAttribPointer(
0, //
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
glEnableVertexAttribArray(0);
glClearColor(0.0,0.0,0.0,0.0);
// Create and compile our GLSL program from the shaders
programID = LoadShaders( "vertex.glsl", "fragment.glsl" );
glViewport(0,0,1280,1280);
// create uniform variables
GLint viewportDimensionsHandle = glGetUniformLocation(programID, "viewportDimensions");
glProgramUniform2i(programID, viewportDimensionsHandle, VIEWPORT_DIMENSION_X, VIEWPORT_DIMENSION_Y);
GLint viewportHandle =glGetUniformLocation(programID, "viewport");
glProgramUniform4i(programID, viewportHandle, 0,0,1280,1280);
// start mainloop
glutMainLoop();
return 0;
}
void glutInitialization(int argc, char **argv) {
// GLUT initializing
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100,100);
glutInitWindowSize(1280,1280);
glutCreateWindow("raycasting");
// register GLUT callbacks
glutDisplayFunc(display);
glutReshapeFunc(resize);
}
void glewInitialization() {
// GLEW initializing
glewInit();
if (glewIsSupported("GL_VERSION_4_3"))
printf("Ready for OpenGL 4.3\n");
else {
printf("OpenGL 4.3 not supported\n");
exit(1);
}
}
void display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use our shader
glUseProgram(programID);
// Bind VAO
glBindVertexArray(VertexArrayID);
// Draw the triangle
glDrawArrays(GL_TRIANGLES, 0, 6); // Starting from vertex 0; 3 vertices total -> 1 triangle
// Unbind VAO
glBindVertexArray(0);
glutSwapBuffers();
}
void resize(int w, int h) {
}
void idle() {
}
void drawSphere(float x, float y, float z) {
glPushMatrix();
}
GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){
// Create the shaders
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Read the Vertex Shader code from the file
std::string VertexShaderCode;
std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
if(VertexShaderStream.is_open())
{
std::string Line = "";
while(getline(VertexShaderStream, Line))
VertexShaderCode += "\n" + Line;
VertexShaderStream.close();
}
// Read the Fragment Shader code from the file
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
if(FragmentShaderStream.is_open()){
std::string Line = "";
while(getline(FragmentShaderStream, Line))
FragmentShaderCode += "\n" + Line;
FragmentShaderStream.close();
}
GLint Result = GL_FALSE;
int InfoLogLength;
// Compile Vertex Shader
printf("Compiling shader : %s\n", vertex_file_path);
char const * VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
glCompileShader(VertexShaderID);
// Check Vertex Shader
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> VertexShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
// Compile Fragment Shader
printf("Compiling shader : %s\n", fragment_file_path);
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
glCompileShader(FragmentShaderID);
// Check Fragment Shader
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);
// Link the program
fprintf(stdout, "Linking program\n");
GLuint ProgramID = glCreateProgram();
glAttachShader(ProgramID, VertexShaderID);
glAttachShader(ProgramID, FragmentShaderID);
glLinkProgram(ProgramID);
// Check the program
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
std::vector<char> ProgramErrorMessage( std::max(InfoLogLength, int(1)) );
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
return ProgramID;
}
I'm sure it's not a complete solution, but it finally draws a sphere:
#version 430 core
uniform ivec2 viewportDimensions;
uniform mat4 gl_ProjectionMatrix;
uniform ivec4 viewport;
uniform float imageAspectRatio;
uniform float angle;
out vec3 color;
struct Ray {
vec3 origin;
vec3 direction;
};
struct Sphere {
vec3 origin;
float radius;
};
bool intersect(Ray r, Sphere s);
void main() {
focal = 60;
angle = tan(focal * 0.5 * 3.1415926535897932384626433832795 / 180); // convert from degree to radian
float xx = (2 * (gl_FragCoord.x + 0.5) / viewportDimensions.x - 1) * angle * imageAspectRatio;
float yy = (1 - 2 * (gl_FragCoord.y + 0.5) / viewportDimensions.y) * angle;
vec3 rayOrigin = vec3(0,0,0);
vec3 rayDirection = normalize(vec3(xx, yy, -1) - rayOrigin);
Ray r;
r.origin = rayOrigin;
r.direction = rayDirection;
Sphere s;
s.origin = vec3(0.0f,0.0f,-1.1f);
s.radius =0.55f;
if (intersect(r,s))
color = vec3(1,0,1);
else
color = vec3(0,0,0);
}
bool intersect(Ray r, Sphere s) {
float a = dot(r.direction,r.direction);
float b = dot(r.direction, 2.0 * (r.origin-s.origin));
float c = dot(s.origin, s.origin) + dot(r.origin,r.origin) +-2.0*dot(r.origin,s.origin) - (s.radius*s.radius);
float disc = b*b + (-4.0)*a*c;
if (disc < 0)
return false;
return true;
}
e.origin = start;
e.direction = normalize(start);
That looks wrong.
Considering this in an Eye-Ray the origin should be the Eye-Position.
e.origin = vec3(0,0,0);
Related
I'm working on a project with OpenGL that uses raymarched graphics and I've run into some trouble controlling the camera.
The scene is generated entirely inside the vertex shader, which is rendered on a 4-vertex plane situated in front of the viewport. My input function gets key-presses and and mouse movement from the user and calculates the translation and rotation of the camera which gets passed into the fragment shader.
The problem is that the rotation and movement of the camera behaves very strangely. Not only does simultaneous pitch and yaw cause the camera to roll (even though I'm using quaternions to calculate rotation), but the camera movement (WASD) seems to only move along the world's X and Z axes rather than the camera's relative axes.
Any help with fixing this would be greatly appreciated.
main.cpp:
#include <iostream>
#include <string>
#include <glad/glad.h>
#include <glfw/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "readshader.h"
#include "input.h"
#define GLM_SWIZZLE_XYZ
#define WIDTH 640
#define HEIGHT 480
using namespace std;
using namespace glm;
int main() {
GLFWwindow* window;
//Vertex array for screen plane
float vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f
};
unsigned int vbo;
unsigned int vao;
unsigned int vs;
unsigned int fs;
unsigned int shader_prog;
//Game logic variables
vec3 camera_pos = vec3(0.0f, 0.0f, 0.0f);
quat camera_rot = quat(0.0f, 0.0f, 0.0f, 0.0f);
vec2 screen_size = vec2(float(WIDTH), float(HEIGHT));
float delta_time = 0.0;
float last_time = 0.0;
unsigned int a_camera_pos;
unsigned int a_camera_rot;
unsigned int a_screen_size;
unsigned int a_delta_time;
//Read shader code from GLSL files
string vs_str = readShader("vert_pass.glsl");
string fs_str = readShader("frag_raymarch_test.glsl");
const char* vs_source = vs_str.c_str();
const char* fs_source = fs_str.c_str();
//Initialize GLFW and create window
glfwInit();
window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glViewport(0, 0, WIDTH, HEIGHT);
//Set GLFW input parameters
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
glfwSetCursorPosCallback(window, mouseCallback);
//Create VAO and VBO
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//Compile shaders and create shader program
vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vs_source, NULL);
glCompileShader(vs);
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fs_source, NULL);
glCompileShader(fs);
shader_prog = glCreateProgram();
glAttachShader(shader_prog, vs);
glAttachShader(shader_prog, fs);
glLinkProgram(shader_prog);
glDetachShader(shader_prog, vs);
glDetachShader(shader_prog, fs);
glDeleteShader(vs);
glDeleteShader(fs);
glUseProgram(shader_prog);
//Setup attribute arrays and uniform variables for vertex shader
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
a_camera_pos = glGetUniformLocation(shader_prog, "vert_camera_pos");
a_camera_rot = glGetUniformLocation(shader_prog, "vert_camera_rot");
a_screen_size = glGetUniformLocation(shader_prog, "vert_screen_size");
a_delta_time = glGetUniformLocation(shader_prog, "vert_delta_time");
//Main event loop
while (!glfwWindowShouldClose(window)) {
//Calculate delta time
delta_time = glfwGetTime() - last_time;
last_time = glfwGetTime();
//Pass game information to shader program
glProgramUniform3fv(shader_prog, a_camera_pos, 1, value_ptr(camera_pos));
glProgramUniform4fv(shader_prog, a_camera_rot, 1, value_ptr(camera_rot));
glProgramUniform2fv(shader_prog, a_screen_size, 1, value_ptr(screen_size));
glProgramUniform1f(shader_prog, a_delta_time, delta_time);
//Process user input
input(window, &camera_pos, &camera_rot, delta_time);
//Rendering procedure
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glfwSwapBuffers(window);
}
//Cleanup and exit
glDeleteProgram(shader_prog);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
input.h:
#pragma once
#include <iostream>
#include <glfw/glfw3.h>
#include <glm/glm.hpp>
#define GLM_SWIZZLE_XYZW
#define SPEED 5.0
#define SENS 0.001
using namespace glm;
float camera_pitch = 0.0;
float camera_yaw = 0.0;
bool first = true;
float lastx = 0.0;
float lasty = 0.0;
float offsetx;
float offsety;
void input(GLFWwindow* window, vec3* camera_pos, quat* camera_rot, float delta_time) {
float step = SPEED * delta_time;
vec3 camera_fwd = vec3(0.0f, 0.0f, -1.0f);
vec3 camera_up = vec3(0.0f, 1.0f, 0.0f);
vec3 camera_right = vec3(-1.0f, 0.0f, 0.0f);
quat quatx;
quat quaty;
glfwPollEvents();
//Build rotation quaternion from camera angles
quatx = angleAxis(camera_yaw, camera_up);
quaty = angleAxis(camera_pitch, camera_right);
*camera_rot = quatx * quaty;
//Update camera axis positions
camera_fwd = *camera_rot * camera_fwd * conjugate(*camera_rot);
camera_right = *camera_rot * camera_right * conjugate(*camera_rot);
//Check keyboard presses
if (glfwGetKey(window, GLFW_KEY_W)) {
*camera_pos -= camera_fwd * step;
}
if (glfwGetKey(window, GLFW_KEY_S)) {
*camera_pos += camera_fwd * step;
}
if (glfwGetKey(window, GLFW_KEY_A)) {
*camera_pos -= normalize(cross(camera_fwd, camera_up)) * step;
}
if (glfwGetKey(window, GLFW_KEY_D)) {
*camera_pos += normalize(cross(camera_fwd, camera_up)) * step;
}
if (glfwGetKey(window, GLFW_KEY_SPACE)) {
*camera_pos += camera_up * step;
}
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL)) {
*camera_pos -= camera_up * step;
}
}
void mouseCallback(GLFWwindow* window, double posx, double posy) {
if (first) {
lastx = -posx;
lasty = -posy;
first = false;
}
offsetx = -posx - lastx;
offsety = lasty + posy;
lastx = -posx;
lasty = -posy;
offsetx *= SENS;
offsety *= SENS;
camera_yaw += offsetx;
camera_pitch += offsety;
if (camera_pitch > 89.0) {
camera_pitch = 89.0;
}
if (camera_pitch < -89.0) {
camera_pitch = -89.0;
}
}
vertex_pass.glsl:
#version 460
//Passes variables from attributes to next shader
layout(location = 0) in vec3 pos;
uniform vec3 vert_camera_pos;
uniform vec4 vert_camera_rot;
uniform vec2 vert_screen_size;
uniform float vert_delta_time;
out vec3 camera_pos;
out vec4 camera_rot;
out vec2 screen_size;
out float delta_time;
void main() {
gl_Position = vec4(pos, 1.0);
camera_pos = vert_camera_pos;
camera_rot = vert_camera_rot;
screen_size = vert_screen_size;
delta_time = vert_delta_time;
}
frag_raymarch_test.glsl:
#version 460
#define MAX_STEPS 100
#define MAX_DIST 10000.0
#define SURF_DIST 0.001
#define NORMAL_SAMPLE_SIZE 0.001
#define TAU 6.283185
#define PI 3.141592
in vec3 camera_pos;
in vec4 camera_rot;
in vec2 screen_size;
in float delta_time;
out vec3 color;
vec3 rotateVector(vec4 quat, vec3 vec) {
return vec + 2.0 * cross(cross(vec, quat.xyz) + quat.w * vec, quat.xyz);
}
//SDF
float getSD(vec3 p) {
vec4 s = vec4(0, 1, 6, 1);
float sphere_dist = length(p - s.xyz) - s.w;
float plane_dist = p.y;
float d = min(sphere_dist, plane_dist);
return d;
}
float getDist(vec3 p) {
float dist = 0;
//SDF
dist = getSD(p);
return dist;
}
float raymarch(vec3 ro, vec3 rd) {
float dist = 0.0;
for(int i = 0; i < MAX_STEPS; i++) {
vec3 p = ro + rd * dist;
dist += getDist(p);
if(dist >= MAX_DIST || dist <= SURF_DIST) {
break;
}
}
return dist;
}
void main() {
vec2 uv = (gl_FragCoord.xy - 0.5 * screen_size) / screen_size.y;
//vec3 ro = vec3(0.0, 1.0, 0.0);
//vec3 ro = camera_pos;
vec3 ro = camera_pos;
vec3 fd = normalize(vec3(uv.x, uv.y, 1.0));
vec3 rd = rotateVector(camera_rot, fd);
color = vec3(raymarch(ro, rd) / 6.0);
}
Figured it out. I went back to using Euler angles instead of quaternions and using them to compute a forward vector for the camera. The problem was that, in the fragment shader, I was using the camera_fwd vector for the look at point when I should have been using camera_pos + camera_fwd.
So I'm drawing some lines by just using a fragment shader. The vertex it's just an empty quad.
The issue I have is that when I zoom out the camera and the lines get smaller they sometimes appear and disappear and I don't understand why.
This is how it looks without zooming
and this is how it looks when the camera is far away from them
The more far away I get from them the more artefacts appear.
This is how my vertex shader looks
#version 330 core
layout(location = 0) in vec2 _position;
out vec2 position;
uniform mat4 uCameraView;
void main() {
gl_Position = uCameraView * vec4(_position.x, _position.y, 0.0f, 1.0f);
position = _position;
}
And this is the fragment
#version 330 core
in vec2 position;
uniform vec4 uGridColor;
uniform float uTileSize;
uniform float uGridBorderSize;
out vec4 fragColor;
void main() {
vec2 uv = mod(position, uTileSize);
vec2 border = mod(uv + (uGridBorderSize / 2.0), uTileSize);
border -= mod(uv - (uGridBorderSize / 2.0), uTileSize);
if (length(border) > uTileSize - uGridBorderSize) {
fragColor = uGridColor;
} else {
fragColor = vec4(0.0);
}
}
Why it's this happening? maybe is something related to antialiasing? My OpenGL setup it's just the default one.
Your current code is making a binary decision "yes line" / "no line". However beyond a certain point (line width < pixel width) you're effectivly dealing with spatial frequencies above the Nyquist limit.
Instead of using a binary "yes"/"no" you need to calculate the pixel coverage, i.e. how much line is inside a pixel. For that you'd normally use a unsigned distance function (UDF). Here's some GLSL code for UDF lines in pixel space (you can also use them in normalized space, but then you'll have to adjust the smoothstep parameters). Try this on https://shadertoy.com
float lsd(vec2 a, vec2 b, vec2 p, float w){
w *= 0.5;
vec2 n = normalize(b-a);
float l = length(b-a);
float t = dot((p-a),n);
float d = length((a-p)+t*n);
float e = min(length(p-a)+w, length(p-b)+w);
return (t > w && t < l-w) ? d : e;
}
float line(vec2 a, vec2 b, float width, vec2 fragcoord){
return max(0., 1.-smoothstep(0., 1., lsd(a, b, fragcoord, width)-0.5*width));
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float l =
line(vec2(8.,8.), vec2(128.,33.), 1., fragCoord)
+ line(vec2(33.,220.), vec2(260.,20.), 4., fragCoord);
fragColor = vec4(l,l,l,1.0);
}
It is hard to tell based on your shaders alone, so here is an example of how to zoom and pan around a line grid. It uses a projection matrix to zoom which looks slightly different to how you implemented the zoom, but the important bit is that it doesn't have any artefacts of lines thinning when zooming/panning.
Here is a demonstration, hopefully the GIF shows it, but the grid lines are constant thickness as you zoom in and out:
#include <iostream>
#include <vector>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
using std::vector;
using glm::mat4;
using glm::vec3;
using glm::vec4;
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;
vec3 rayCast(double xpos, double ypos, mat4 projection, mat4 view) {
// converts a position from the 2d xpos, ypos to a normalized 3d direction
float x = (2.0f * xpos) / SCR_WIDTH - 1.0f;
float y = 1.0f - (2.0f * ypos) / SCR_HEIGHT;
float z = 1.0f;
vec3 ray_nds = vec3(x, y, z);
vec4 ray_clip = vec4(ray_nds.x, ray_nds.y, -1.0f, 1.0f);
// eye space to clip we would multiply by projection so
// clip space to eye space is the inverse projection
vec4 ray_eye = inverse(projection) * ray_clip;
// convert point to forwards
ray_eye = vec4(ray_eye.x, ray_eye.y, -1.0f, 0.0f);
// world space to eye space is usually multiply by view so
// eye space to world space is inverse view
vec4 inv_ray_wor = (inverse(view) * ray_eye);
vec3 ray_wor = vec3(inv_ray_wor.x, inv_ray_wor.y, inv_ray_wor.z);
ray_wor = normalize(ray_wor);
return ray_wor;
}
class Line {
int shaderProgram;
unsigned int VBO, VAO;
vector<float> vertices;
vec3 startPoint;
vec3 endPoint;
mat4 MVP;
vec3 lineColor;
public:
Line(vec3 start, vec3 end) {
startPoint = start;
endPoint = end;
lineColor = vec3(1,1,1);
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"uniform mat4 MVP;\n"
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec3 color;\n"
"void main()\n"
"{\n"
" FragColor = vec4(color, 1.0f);\n"
"}\n\0";
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// link shaders
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
vertices = {
start.x, start.y, start.z,
end.x, end.y, end.z,
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
int setMVP(mat4 mvp) {
MVP = mvp;
}
int setColor(vec3 color) {
lineColor = color;
}
int draw() {
glUseProgram(shaderProgram);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]);
glUniform3fv(glGetUniformLocation(shaderProgram, "color"), 1, &lineColor[0]);
glBindVertexArray(VAO);
glDrawArrays(GL_LINES, 0, 2);
return 0;
}
~Line() {
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
}
};
vec3 cameraPos = glm::vec3(0.0f, 0.0f, 15.0f);
vec3 cameraFront = glm::vec3(0,0,-1);
mat4 model = mat4(1.0);
glm::mat4 view;
glm::mat4 projection;
float scrollSpeed = 2.0f;
float fov = 45.0f;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "grid", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Line x(vec3(0,0,0), vec3(1,0,0));
x.setColor(vec3(1,0,0));
Line y(vec3(0,0,0), vec3(0,1,0));
y.setColor(vec3(0,1,0));
std::vector<Line*> grid = {};
for (int i = -5; i < 6; i++) {
grid.push_back(new Line(vec3(-5, i, 0), vec3(5,i, 0)));
}
for (int j = -5; j < 6; j++) {
grid.push_back(new Line(vec3(j, -5, 0), vec3(j,5, 0)));
};
while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
view = glm::lookAt(cameraPos, cameraPos + cameraFront, vec3(0,1,0));
projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
for (int i = 0; i < grid.size(); i++) {
grid[i]->setMVP(projection * view * model);
grid[i]->draw();
}
glfwSwapBuffers(window);
glfwPollEvents();
}
for (int i = 0; i < grid.size(); i++) {
delete grid.at(i);
}
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE);
if (state == GLFW_PRESS)
{
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
cameraPos -= scrollSpeed * glm::vec3(xoffset/(float)SCR_WIDTH, yoffset/(float)SCR_WIDTH, 0);
} else {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
firstMouse = true;
}
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
cameraPos += (float)yoffset * rayCast(lastX, lastY, projection, view);
}
I am trying to draw filled circle in modern opengl using GL_TRIANGLE. I am filling circle array for circle vertices, and then I am filling points array using circle vertices to use vertices in triangle.I am using vec4(homogeneous coordinates) and setting vertices z-axis value to 0.0 and using default projection which should be from -1 to 1 ranges for each axis. But somehow, I am getting all screen painted with color instead of circle. Weirdly when I set z-axis value of points other than [0-0.4] range, I can get circle be drawn and when I increase the value of z, it acts like perspective and no matter what I have set to radius, it is getting smaller when z is increased. I could not figure out why z value acts like that.
using namespace Angel;
const int NumVertices = 90;
const double PI = 3.141592653589793238463;
const GLint width = 500;
const GLint height = 500;
vec4 points[NumVertices];
vec4 colors[NumVertices];
vec4 circle[30];
vec4 color = { 1.0,0.0,0.0,1.0 };
vec4 center = { 0.0,0.0,0.0,1.0 };
GLfloat radius = 0.5;
int Index = 0;
void triangle(int b, int c) {
colors[Index] = color; points[Index] = center; Index++;
colors[Index] = color; points[Index] = circle[b%30]; Index++;
colors[Index] = color; points[Index] = circle[c%30]; Index++;
}
void fill() {
for (int i = 0; i < 30; i++)
{
float angle = 2 * PI * i / 30;
vec4 point;
point.x = center.x + (GLfloat)cos(angle) * radius;
point.y = center.y + (GLfloat)sin(angle) * radius;
point.z = 0.0 ; // PROBLEM !!
circle[i] = point;
}
for (int i = 0; i <= 29; i++) {
triangle(i, i + 1);
}
}
void init()
{
fill();
// Create a vertex array object
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create and initialize a buffer object
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(points), points);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors);
// Load shaders and use the resulting shader program
GLuint program = InitShader("vshader.glsl", "fshader.glsl");
glUseProgram(program);
// set up vertex arrays
GLuint vPosition = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(vPosition);
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0));
GLuint vColor = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(vColor);
glVertexAttribPointer(vColor, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(sizeof(points)));
glEnable(GL_DEPTH_TEST);
glClearColor(1.0, 1.0, 1.0, 1.0);
}
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, NumVertices);
glutSwapBuffers();
}
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(width, height);
glutInitWindowPosition(100, 100);
glutCreateWindow("PirateFace");
glewExperimental = GL_TRUE;
glewInit();
init();
glutDisplayFunc(myDisplay);
glutMainLoop();
return 0;
}
vertex shader :
#version 150
in vec4 vPosition;
in vec4 vColor;
out vec4 color;
void main()
{
gl_Position = vPosition;
color = vColor;
}
fragment shader :
#version 150
in vec4 color;
out vec4 fColor;
void main()
{
fColor = color;
}
I am using vec4(homogeneous coordinates)
You need to properly initialize the w component of your vec4 values to 1.0.
Note that you do not need to store homogenous coordinates in your input vertex arrays in most scenarios. You can keep the in vec4 in the vertex shader and just use 3-dimensional vectors as input, and the GL will automatically extend the vector to (x,y,z,1).
Im wondering how to rotate a OpenGL triangle around its Y-Axis in OpenGL,
Ive been able to translate the triangle from object space to clip space.
Im wondering if anyone has any experience with this issue and may be able to lend a hand.
Main.cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <Windows.h>
using namespace std;
using namespace glm;
const int windowWidth = 1024;
const int windowHeight = 768;
GLuint VBO;
const int NUMVERTS = 3;
GLuint gModelToWorldTransformLocation;
GLuint gWorldToViewTransformLocation;
GLuint gProjectionTransformLocation;
struct SimpleVertex
{
vec3 pos;
vec4 colour;
};
static void renderSceneCallBack()
{
static mat4 modelToWorldTransform = mat4(1.0f);
static mat4 worldToViewTransform = lookAt(
vec3(0.0f,0.0f,3.0f), // position of your camera, in world space
vec3(0.0f,0.0f,0.0f), // look at in world space
vec3(0.0f,1.0f,0.0f) // Camera up direction (set to 0,-1,0 to look upside-down)
);
static mat4 projectionTransform = perspective(45.0f, (float)windowWidth / (float)windowHeight, 1.0f, 100.0f);
glUniformMatrix4fv(gModelToWorldTransformLocation, 1, GL_FALSE, &modelToWorldTransform[0][0]);
glUniformMatrix4fv(gWorldToViewTransformLocation, 1, GL_FALSE, &worldToViewTransform[0][0]);
glUniformMatrix4fv(gProjectionTransformLocation, 1, GL_FALSE, &projectionTransform[0][0]);
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(SimpleVertex), 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(SimpleVertex), (const GLvoid*)12);
glDrawArrays(GL_TRIANGLES, 0, NUMVERTS);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glutSwapBuffers();
}
static void initializeGlutCallbacks()
{
glutDisplayFunc(renderSceneCallBack);
glutIdleFunc(renderSceneCallBack);
}
static void createVertexBuffer()
{
// Create some vertices to put in our VBO.
// Create vertex buffer
SimpleVertex vertices[] =
{
{vec3(-0.5f, -0.5f, 0.0f), vec4(1.0f, 0.0f, 0.0f, 1.0f)},
{vec3(0.5f, -0.5f, 0.0f), vec4(0.0f, 1.0f, 0.0f, 1.0f)},
{vec3( 0.0f, 0.5f, 0.0f), vec4(0.0f, 0.0f, 1.0f, 1.0f)}
};
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(SimpleVertex) * 3, vertices, GL_STATIC_DRAW);
}
static void addShader(GLuint shaderProgram, const char* pShaderText, GLenum shaderType)
{
GLuint shaderObj = glCreateShader(shaderType);
if (shaderObj == 0)
{
cerr<<"Error creating shader type "<<shaderType<<endl;
exit(0);
}
const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0]= strlen(pShaderText);
glShaderSource(shaderObj, 1, p, Lengths);
glCompileShader(shaderObj);
GLint success;
glGetShaderiv(shaderObj, GL_COMPILE_STATUS, &success);
if (!success)
{
GLchar InfoLog[1024];
glGetShaderInfoLog(shaderObj, 1024, NULL, InfoLog);
cerr<<"Error compiling shader type "<<shaderType<<": "<<InfoLog<<endl;
exit(1);
}
glAttachShader(shaderProgram, shaderObj);
}
const string readFileToString(char* filename)
{
ifstream file (filename, ios::in);
if (file.is_open())
{
stringstream continut;
continut << file.rdbuf();
continut << '\0';
return continut.str();
}
return "";
}
static void buildShaders()
{
GLuint shaderProgram = glCreateProgram();
if (shaderProgram == 0)
{
cerr<<"Error creating shader program\n";
exit(1);
}
string VS = readFileToString("vertexShader.glsl");
string FS = readFileToString("fragmentShader.glsl");
addShader(shaderProgram, VS.c_str(), GL_VERTEX_SHADER);
addShader(shaderProgram, FS.c_str(), GL_FRAGMENT_SHADER);
GLint success = 0;
GLchar errorLog[1024] = { 0 };
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (success == 0)
{
glGetProgramInfoLog(shaderProgram, sizeof(errorLog), NULL, errorLog);
cerr<<"Error linking shader program: "<<errorLog<<endl;
exit(1);
}
glValidateProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_VALIDATE_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, sizeof(errorLog), NULL, errorLog);
cerr<<"Error linking shader program: "<<errorLog<<endl;
exit(1);
}
glUseProgram(shaderProgram);
gModelToWorldTransformLocation = glGetUniformLocation(shaderProgram, "gModelToWorldTransform");
//assert(gModelToWorldTransformLocation != 0xFFFFFFFF);
gWorldToViewTransformLocation = glGetUniformLocation(shaderProgram, "gWorldToViewTransform");
//assert(gWorldToViewTransformLocation != 0xFFFFFFFF);
gProjectionTransformLocation = glGetUniformLocation(shaderProgram, "gProjectionTransform");
//assert(gProjectionTransformLocation != 0xFFFFFFFF);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutInitWindowSize(windowWidth, windowHeight);
glutInitWindowPosition(100, 100);
glutCreateWindow("Transformations");
initializeGlutCallbacks();
// Must be done after glut is initialized!
GLenum res = glewInit();
if (res != GLEW_OK)
{
cerr<<"Error: "<<glewGetErrorString(res)<<"\n";
return 1;
}
buildShaders();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// Create a vertex buffer
createVertexBuffer();
glutMainLoop();
return 0;
}
VertexShader
#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec4 Colour;
out vec4 Colour0;
uniform mat4 gModelToWorldTransform;
uniform mat4 gWorldToViewTransform;
uniform mat4 gProjectionTransform;
void main()
{
vec4 vertexPositionInModelSpace = vec4(Position, 1);
vec4 vertexInWorldSpace = gModelToWorldTransform * vertexPositionInModelSpace;
vec4 vertexInViewSpace = gWorldToViewTransform * vertexInWorldSpace;
vec4 vertexInHomogeneousClipSpace = gProjectionTransform * vertexInViewSpace;
gl_Position = vertexInHomogeneousClipSpace;
Colour0 = Colour;
}
FragmentShader
#version 330
in vec4 Colour0;
out vec4 FragColor;
void main()
{
FragColor = Colour0;
}
You can have a float variable for rotation and create rotation matrix from it once then store it. Every frame you should have the world matrix updated like this: modelToWorldTransform = rotationTransform * modelToWorldTransform; See wikipedia for creating the rotation matrix around y: http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations
EDIT: I suppose your having trouble with affine transformations. To move (no matter how - translate, rotate) an object in the world, you have to apply a transformation to it.
Currently, you are applying one transformation, which is translation to the position 0, 0, 3.
So far, so good. But your triangle is static(it doesn't move). So to rotate it, you need to apply another transformation (represented by a matrix, again). But if you always apply one and the same rotation, the triangle will be rotated against its original transform but it won't move once again. So you need to apply a rotation every frame, store the result in the modeltoworldtransform and then on next frame repeat the step. e.g in the renderCallback:
static mat4 modelToWorldTransform = mat4(1.0f);
static mat4 rotationTransform = <rotation by the described in wiki way>;
modelToWorldTransform = rotationTransform * modelToWorldTransform;
Also, order of multiplying matrices matters, because their multiplication (which we use to get transform from object to camera space) is not commutative and A * B is different from B * A. I highly recommend you to read more about linear algebra and it will help you understand what happens "behind the scenes". Many books about graphics for game devs explain it in a pretty good and understandable way. "Mathematics for 3D Game Programming and Computer Graphics" by Eric Lengyel kicks ass. It even helped me when I studied maths in university ;).
I'm having trouble getting phong shading to look right. I'm pretty sure there's something wrong with either my OpenGL calls, or the way I'm loading my normals, but I guess it could be something else since 3D graphics and Assimp are both still very new to me. When trying to load .obj/.mtl files, the problems I'm seeing are:
The models seem to be lit too intensely (less phong-style and more completely washed out, too bright).
Faces that are lit seem to be lit equally all over (with the exception of a specular highlight showing only when the light source position is moved to be practically right on top of the model)
Because of problems 1 and 2, spheres look very wrong:
picture of sphere
And things with larger faces look (less-noticeably) wrong too:
picture of cube
I could be wrong, but to me this doesn't look like proper phong shading.
Here's the code that I think might be relevant (I can post more if necessary):
file: assimpRenderer.cpp
#include "assimpRenderer.hpp"
namespace def
{
assimpRenderer::assimpRenderer(std::string modelFilename, float modelScale)
{
initSFML();
initOpenGL();
if (assImport(modelFilename)) // if modelFile loaded successfully
{
initScene();
mainLoop(modelScale);
shutdownScene();
}
shutdownOpenGL();
shutdownSFML();
}
assimpRenderer::~assimpRenderer()
{
}
void assimpRenderer::initSFML()
{
windowWidth = 800;
windowHeight = 600;
settings.majorVersion = 3;
settings.minorVersion = 3;
app = NULL;
shader = NULL;
app = new sf::Window(sf::VideoMode(windowWidth,windowHeight,32), "OpenGL 3.x Window", sf::Style::Default, settings);
app->setFramerateLimit(240);
app->setActive();
return;
}
void assimpRenderer::shutdownSFML()
{
delete app;
return;
}
void assimpRenderer::initOpenGL()
{
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
}
// check the OpenGL context version that's currently in use
int glVersion[2] = {-1, -1};
glGetIntegerv(GL_MAJOR_VERSION, &glVersion[0]); // get the OpenGL Major version
glGetIntegerv(GL_MINOR_VERSION, &glVersion[1]); // get the OpenGL Minor version
std::cout << "Using OpenGL Version: " << glVersion[0] << "." << glVersion[1] << std::endl;
return;
}
void assimpRenderer::shutdownOpenGL()
{
return;
}
void assimpRenderer::initScene()
{
// allocate heap space for VAOs, VBOs, and IBOs
vaoID = new GLuint[scene->mNumMeshes];
vboID = new GLuint[scene->mNumMeshes*2];
iboID = new GLuint[scene->mNumMeshes];
glClearColor(0.4f, 0.6f, 0.9f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
shader = new Shader("shader.vert", "shader.frag");
projectionMatrix = glm::perspective(60.0f, (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
rot = 0.0f;
rotSpeed = 50.0f;
faceIndex = 0;
colorArrayA = NULL;
colorArrayD = NULL;
colorArrayS = NULL;
normalArray = NULL;
genVAOs();
return;
}
void assimpRenderer::shutdownScene()
{
delete [] iboID;
delete [] vboID;
delete [] vaoID;
delete shader;
}
void assimpRenderer::renderScene(float modelScale)
{
sf::Time elapsedTime = clock.getElapsedTime();
clock.restart();
if (rot > 360.0f)
rot = 0.0f;
rot += rotSpeed * elapsedTime.asSeconds();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -3.0f, -10.0f)); // move back a bit
modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(modelScale)); // scale model
modelMatrix = glm::rotate(modelMatrix, rot, glm::vec3(0, 1, 0));
//modelMatrix = glm::rotate(modelMatrix, 25.0f, glm::vec3(0, 1, 0));
glm::vec3 lightPosition( 0.0f, -100.0f, 0.0f );
float lightPositionArray[3];
lightPositionArray[0] = lightPosition[0];
lightPositionArray[1] = lightPosition[1];
lightPositionArray[2] = lightPosition[2];
shader->bind();
int projectionMatrixLocation = glGetUniformLocation(shader->id(), "projectionMatrix");
int viewMatrixLocation = glGetUniformLocation(shader->id(), "viewMatrix");
int modelMatrixLocation = glGetUniformLocation(shader->id(), "modelMatrix");
int ambientLocation = glGetUniformLocation(shader->id(), "ambientColor");
int diffuseLocation = glGetUniformLocation(shader->id(), "diffuseColor");
int specularLocation = glGetUniformLocation(shader->id(), "specularColor");
int lightPositionLocation = glGetUniformLocation(shader->id(), "lightPosition");
int normalMatrixLocation = glGetUniformLocation(shader->id(), "normalMatrix");
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]);
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &modelMatrix[0][0]);
glUniform3fv(lightPositionLocation, 1, lightPositionArray);
for (unsigned int i = 0; i < scene->mNumMeshes; i++)
{
colorArrayA = new float[3];
colorArrayD = new float[3];
colorArrayS = new float[3];
material = scene->mMaterials[scene->mNumMaterials-1];
normalArray = new float[scene->mMeshes[i]->mNumVertices * 3];
unsigned int normalIndex = 0;
for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
{
normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
}
normalIndex = 0;
glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);
aiColor3D ambient(0.0f, 0.0f, 0.0f);
material->Get(AI_MATKEY_COLOR_AMBIENT, ambient);
aiColor3D diffuse(0.0f, 0.0f, 0.0f);
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
aiColor3D specular(0.0f, 0.0f, 0.0f);
material->Get(AI_MATKEY_COLOR_SPECULAR, specular);
colorArrayA[0] = ambient.r; colorArrayA[1] = ambient.g; colorArrayA[2] = ambient.b;
colorArrayD[0] = diffuse.r; colorArrayD[1] = diffuse.g; colorArrayD[2] = diffuse.b;
colorArrayS[0] = specular.r; colorArrayS[1] = specular.g; colorArrayS[2] = specular.b;
// bind color for each mesh
glUniform3fv(ambientLocation, 1, colorArrayA);
glUniform3fv(diffuseLocation, 1, colorArrayD);
glUniform3fv(specularLocation, 1, colorArrayS);
// render all meshes
glBindVertexArray(vaoID[i]); // bind our VAO
glDrawElements(GL_TRIANGLES, scene->mMeshes[i]->mNumFaces*3, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); // unbind our VAO
delete [] normalArray;
delete [] colorArrayA;
delete [] colorArrayD;
delete [] colorArrayS;
}
shader->unbind();
app->display();
return;
}
void assimpRenderer::handleEvents()
{
sf::Event event;
while (app->pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
app->close();
}
if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
{
app->close();
}
if (event.type == sf::Event::Resized)
{
glViewport(0, 0, event.size.width, event.size.height);
}
}
return;
}
void assimpRenderer::mainLoop(float modelScale)
{
while (app->isOpen())
{
renderScene(modelScale);
handleEvents();
}
}
bool assimpRenderer::assImport(const std::string& pFile)
{
// read the file with some example postprocessing
scene = importer.ReadFile(pFile,
aiProcess_CalcTangentSpace |
aiProcess_Triangulate |
aiProcess_JoinIdenticalVertices |
aiProcess_SortByPType);
// if the import failed, report it
if (!scene)
{
std::cerr << "Error: " << importer.GetErrorString() << std::endl;
return false;
}
return true;
}
void assimpRenderer::genVAOs()
{
int vboIndex = 0;
for (unsigned int i = 0; i < scene->mNumMeshes; i++, vboIndex+=2)
{
mesh = scene->mMeshes[i];
indexArray = new unsigned int[mesh->mNumFaces * sizeof(unsigned int) * 3];
// convert assimp faces format to array
faceIndex = 0;
for (unsigned int t = 0; t < mesh->mNumFaces; ++t)
{
const struct aiFace* face = &mesh->mFaces[t];
std::memcpy(&indexArray[faceIndex], face->mIndices, sizeof(float) * 3);
faceIndex += 3;
}
// generate VAO
glGenVertexArrays(1, &vaoID[i]);
glBindVertexArray(vaoID[i]);
// generate IBO for faces
glGenBuffers(1, &iboID[i]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mesh->mNumFaces * 3, indexArray, GL_STATIC_DRAW);
// generate VBO for vertices
if (mesh->HasPositions())
{
glGenBuffers(1, &vboID[vboIndex]);
glBindBuffer(GL_ARRAY_BUFFER, vboID[vboIndex]);
glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(GLfloat) * 3, mesh->mVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray((GLuint)0);
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
// generate VBO for normals
if (mesh->HasNormals())
{
normalArray = new float[scene->mMeshes[i]->mNumVertices * 3];
unsigned int normalIndex = 0;
for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
{
normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
}
normalIndex = 0;
glGenBuffers(1, &vboID[vboIndex+1]);
glBindBuffer(GL_ARRAY_BUFFER, vboID[vboIndex+1]);
glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(GLfloat) * 3, normalArray, GL_STATIC_DRAW);
glEnableVertexAttribArray((GLuint)1);
glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);
delete [] normalArray;
}
// tex coord stuff goes here
// unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
delete [] indexArray;
}
vboIndex = 0;
return;
}
}
file: shader.vert
#version 150 core
in vec3 in_Position;
in vec3 in_Normal;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform vec3 lightPosition;
uniform mat3 normalMatrix;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
void main()
{
// derive MVP and MV matrices
mat4 modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix;
mat4 modelViewMatrix = viewMatrix * modelMatrix;
// get surface normal in eye coordinates
vVaryingNormal = normalMatrix * in_Normal;
// get vertex position in eye coordinates
vec4 vPosition4 = modelViewMatrix * vec4(in_Position, 1.0);
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
// get vector to light source
vVaryingLightDir = normalize(lightPosition - vPosition3);
// Set the position of the current vertex
gl_Position = modelViewProjectionMatrix * vec4(in_Position, 1.0);
}
file: shader.frag
#version 150 core
out vec4 out_Color;
uniform vec3 ambientColor;
uniform vec3 diffuseColor;
uniform vec3 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
void main()
{
// dot product gives us diffuse intensity
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
// multiply intensity by diffuse color, force alpha to 1.0
out_Color = vec4(diff * diffuseColor, 1.0);
// add in ambient light
out_Color += vec4(ambientColor, 1.0);
// specular light
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if (diff != 0)
{
float fSpec = pow(spec, 128.0);
// Set the output color of our current pixel
out_Color.rgb += vec3(fSpec, fSpec, fSpec);
}
}
I know it's a lot to look through, but I'm putting most of the code up so as not to assume where the problem is.
Are you doing the right thing for the normal matrix? This looks quite bizarre to me.
for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
{
normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
}
glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);
Why does the normalMatrix have anything to do with the vertices of the mesh? It should be identical to your modelMatrix (provided that you're not doing any non-uniform scaling).