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);
}
Related
I am rendering 3d scene with OpenGL. Users are allowed to orbit the scene. Besides the objects in the scene, I want to draw a axis indicator at the top right or bottom left corner to show the current roation status.
Something like the viewport widget at the top right corner in Blender.
Can anyone tell me the direction to do it?
The way I would do it is to:
First- Render your scene;
Second- Render the axis indicator (view cube) on its own to a texture attachment in a frame buffer.
Third- Render the resulting texture to a quad in the top-right of your screen.
Something similar to this: https://learnopengl.com/Advanced-OpenGL/Framebuffers
Very briefly explained, once you have a 3D axis, you start by drawing the 3D axis at camera.Position + camera.Front * smallDist, so it always draws at the center of the screen.
Then, you use glViewport(...) to set the viewport to the corner of the screen, and draw the axis. You can also use eg. glLineWidth(3.0f) to make the lines stand out more.
Here's a rough idea:
#include <iostream>
#include <vector>
#include <math.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/ext.hpp>
// camera.h is from basic camera learnopengl tutorial
#include "camera.h"
using glm::mat4;
using glm::vec3;
using glm::radians;
using glm::lookAt;
using std::vector;
void processInput(GLFWwindow *window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// 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; // time between current frame and last frame
float lastFrame = 0.0f;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
class Line {
int shaderProgram;
unsigned int VBO, VAO;
vector<float> vertices;
vec3 startPoint;
vec3 endPoint;
mat4 MVP = mat4(1.0);
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
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
// link shaders
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
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.size(), 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;
return 1;
}
int setColor(vec3 color) {
lineColor = color;
return 1;
}
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 1;
}
~Line() {
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
}
};
int main()
{
// glfw: initialize and configure
// ------------------------------
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
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "drawing lines", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwMakeContextCurrent(window);
glfwSetCursorPosCallback(window, mouse_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
camera.Position = vec3(0,0,3);
camera.MovementSpeed = 0.01f;
// 3d lines example
Line line1(vec3(0,0,0), vec3(0.04,0,0));
line1.setColor(vec3(1,0,0));
Line line2(vec3(0,0,0), vec3(0,0.04,0));
line2.setColor(vec3(0,1,0));
Line line3(vec3(0,0,0), vec3(0,0,0.04));
line3.setColor(vec3(0,0,1));
Line line4(vec3(0,0,0), vec3(10,0,0));
line4.setColor(vec3(1,0,0));
Line line5(vec3(0,0,0), vec3(0,10,0));
line5.setColor(vec3(0,1,0));
Line line6(vec3(0,0,0), vec3(0,0,10));
line6.setColor(vec3(0,0,1));
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float) SCR_WIDTH / (float)SCR_HEIGHT, 0.01f, 1000.0f);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.01f, 1000.0f);
glm::mat4 view = camera.GetViewMatrix();
float ar = (float)SCR_WIDTH / (float)SCR_HEIGHT;
float fov = 45.0f;
float nearDist = 0.01f;
float Hnear = 2.0f * tan(glm::radians(fov/2)) * nearDist;
float Wnear = Hnear * ar;
glm::vec3 axisPosition = camera.Position + glm::normalize(camera.Front) * 0.2f;
line1.setMVP(projection * view * glm::translate(glm::mat4(1.0f), axisPosition));
line2.setMVP(projection * view * glm::translate(glm::mat4(1.0f), axisPosition));
line3.setMVP(projection * view * glm::translate(glm::mat4(1.0f), axisPosition));
glViewport(SCR_WIDTH-SCR_WIDTH/10.0f,SCR_HEIGHT-SCR_HEIGHT/10.0f,SCR_WIDTH/10.0f,SCR_HEIGHT/10.0f);
glLineWidth(3.0f);
line1.draw();
line2.draw();
line3.draw();
glLineWidth(1.0f);
glViewport(0,0,SCR_WIDTH, SCR_HEIGHT);
line4.setMVP(projection * view);
line5.setMVP(projection * view);
line6.setMVP(projection * view);
line4.draw();
line5.draw();
line6.draw();
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
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; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
Result:
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.
I'm trying to show the normals of some vertices of the mesh when I select them but accordingly to the position of the cam these normals are displayed or not and the big problem is that they are displayed when they shouldn't (i.e. when i select vertices of the tibia but i'm looking at the calf so they should be hidden by the leg) and not displayed when they should be displayed.
I could be wrong but it's like some geometry of the model is drawn above the line. Moreover, if before calling the draw calls i disable the depth test it works (even if the problem of being showed when they shouldn't isn't resolved obviously). That's some images to give you an idea of what I mean.
WITH DEPTH TEST OFF:
DEPT TEST ON:
That's the code:
void StatusManager::Render() {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!animatedModel)
return;
Update();
// draw wireframe if enabled
if (wireframeEnabled)
DrawWireframe();
if (visualMode == Mode_Texture) {
// draw model
if (lightingMode == Mode_Flat)
DrawModel(modelFlatShader);
else if (lightingMode == Mode_Smooth)
DrawModel(modelSmoothShader);
else
DrawModel(modelNoLightShader);
}
else if (visualMode == Mode_CurrentBoneIDInfluence) {
DrawModel(currentBoneShader);
}
else if (visualMode == Mode_NumBones)
{
DrawModel(numBonesShader);
}
else
{
DrawModel(modelGreyShader);
}
// render selected vertices
DrawSelectedVertices();
if (!info.hitPoint)
return;
//DrawHotPoint();
if (selectionMode == Mode_Vertex)
DrawHoveredPoint();
if (selectionMode == Mode_Edge)
DrawHoveredLine();
if (selectionMode == Mode_Face)
DrawHoveredFace();
}
void StatusManager::DrawWireframe() {
wireframeShader.use();
glLineWidth(1.0f);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
wireframeShader.setMat4("modelView", modelView);
wireframeShader.setMat4("projection", projection);
// pass bones matrices to the shader
auto transforms = animator.GetFinalBoneMatrices();
for (int i = 0; i < transforms.size(); ++i)
wireframeShader.setMat4("finalBonesMatrices[" + std::to_string(i) + "]", transforms[i]);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
animatedModel.value().Draw(wireframeShader);
}
void StatusManager::DrawModel(Shader& modelShader) {
modelShader.use();
// model/view/projection transformations
modelShader.setMat4("modelView", camera.viewMatrix);
modelShader.setMat4("projection", projection);
if (visualMode == Mode_Texture)
modelShader.setVec3("light_pos", lightPos);
else if (visualMode == Mode_CurrentBoneIDInfluence)
modelShader.setInt("currentBoneID", currentBoneID);
// pass bones matrices to the shader
auto transforms = animator.GetFinalBoneMatrices();
for (int i = 0; i < transforms.size(); ++i)
modelShader.setMat4("finalBonesMatrices[" + std::to_string(i) + "]", transforms[i]);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
/*if (pause)
bakedModel.value().Draw(modelShader);
else*/
animatedModel.value().Draw(modelShader);
}
void StatusManager::DrawHoveredFace() {
assert(bakedModel.has_value());
assert(info.hitPoint.has_value());
Mesh& m = bakedModel.value().meshes[info.meshIndex];
Face& f = info.face.value();
//vertex
float hoveredVertices[9] = {
m.vertices[f.indices[0]].Position.x, m.vertices[f.indices[0]].Position.y, m.vertices[f.indices[0]].Position.z,
m.vertices[f.indices[1]].Position.x, m.vertices[f.indices[1]].Position.y, m.vertices[f.indices[1]].Position.z,
m.vertices[f.indices[2]].Position.x, m.vertices[f.indices[2]].Position.y, m.vertices[f.indices[2]].Position.z,
};
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(hoveredVertices), &hoveredVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glDrawArrays(GL_TRIANGLES, 0, 3);
//unbind
glBindVertexArray(0);
}
void StatusManager::DrawHoveredPoint() {
assert(bakedModel.has_value());
assert(info.hitPoint.has_value());
Mesh& m = bakedModel.value().meshes[info.meshIndex];
Face& f = info.face.value();
int index = getClosestVertexIndex(info.hitPoint.value(), m, f);
//vertex
float hoveredVertices[3] = { m.vertices[index].Position.x, m.vertices[index].Position.y, m.vertices[index].Position.z };
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 3 * sizeof(float), &hoveredVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glPointSize(8.0f);
glDrawArrays(GL_POINTS, 0, 1);
//unbind
glBindVertexArray(0);
}
void StatusManager::DrawHotPoint()
{
float hotVertices[3] = { hotPoint.x, hotPoint.y, hotPoint.z };
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, 3 * sizeof(float), &hotVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glPointSize(8.0f);
glDrawArrays(GL_POINTS, 0, 1);
//unbind
glBindVertexArray(0);
}
void StatusManager::UpdateSelectedVertices()
{
selectedVertices.clear();
for (Vertex* v : selectedVerticesPointers)
selectedVertices.push_back(*v);
}
void StatusManager::DrawHoveredLine() {
assert(bakedModel.has_value());
assert(info.hitPoint.has_value());
Mesh& m = bakedModel.value().meshes[info.meshIndex];
Face& f = info.face.value();
auto line = getClosestLineIndex(info.hitPoint.value(), m, f);
//vertex
float hoveredVertices[6] = {
m.vertices[line.v1].Position.x, m.vertices[line.v1].Position.y, m.vertices[line.v1].Position.z,
m.vertices[line.v2].Position.x, m.vertices[line.v2].Position.y, m.vertices[line.v2].Position.z
};
hoverShader.use();
glBindVertexArray(HVAO);
glBindBuffer(GL_ARRAY_BUFFER, HVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(hoveredVertices), &hoveredVertices);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0, 0.0);
// model/view/projection transformations
glm::mat4 modelView = camera.viewMatrix;
hoverShader.setMat4("modelView", modelView);
hoverShader.setMat4("projection", projection);
glLineWidth(3.0f);
glDrawArrays(GL_LINES, 0, 2);
glLineWidth(1.0f);
//unbind
glBindVertexArray(0);
}
normalShader just for completeness
VERTEX SHADER:
#version 330 core
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in ivec4 boneIds;
layout(location = 3) in vec4 weights;
layout(location = 4) in int numBones;
uniform mat4 projection;
uniform mat4 modelView;
uniform mat4 finalBonesMatrices[100];
out VS_OUT {
vec4 normal;
} vs_out;
void main()
{
mat4 cumulativeMatrix = mat4(1.0);
if (numBones>0)
cumulativeMatrix = mat4(0.0);
for(int i = 0 ; i < numBones ; i++)
{
cumulativeMatrix += (finalBonesMatrices[boneIds[i]] * weights[i]);
}
gl_Position = projection * modelView * cumulativeMatrix * vec4(pos, 1.0);
vs_out.normal = normalize(modelView * cumulativeMatrix * vec4(norm, 0.0));
}
GEOMETRY SHADER:
#version 330 core
layout (points) in;
layout (line_strip, max_vertices = 2) out;
uniform float normal_length;
in VS_OUT {
vec4 normal;
} gs_in[];
void main() {
gl_Position = gl_in[0].gl_Position;
EmitVertex();
gl_Position = gl_in[0].gl_Position + gs_in[0].normal * normal_length;
EmitVertex();
EndPrimitive();
}
FRAGMENT SHADER:
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1, 0, 0, 1);
}
If you need other piece of code tell me and I'll provide it, but I think I posted all the code and images you need.
Ok I don't know how I didn't noticed it, but yes, the problem was that I wasn't applying the projection matrix to the normal vector. Thanks Spektre to point this out.
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);
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).