I am working on an OpenGL project. In this project, I would like to simulate a 3D chess game in c++. Of course, the pieces should be movable at will. The chessboard and pieces are already displayed with textures included.
I have been working on transforming the pieces. In my model class, all important parameters such as vertices, indices, numvertices, numindices and more are read from the respective model. This happens in the constructor of this class.
I also create an identity matrix (glm::mat4(1.0f)) for each model in the constructor of the model class.
After all the parameters have been read, I push them into a vector<Mesh*>.
In the mesh class, the vertices and indices are first loaded into the vertex buffer/index buffer in the constructor.
The location of the uniform variable in the vertex shader for my model matrix is also calculated in the constructor.
In the render function of the mesh class, the vertex buffer/index buffer is bound and all uniforms are set. At the end of the function, I call glDrawElements.
In the vertex shader, I multiply the model matrix, the viewProjectionMatrix, and vec4(a_position, 1.0f) into gl_Position.
If the identity matrix in the constructor of the model class is glm::mat4(1.0f), the model is displayed normally.
But if I transform, scale or rotate the matrix in the constructor for testing purposes, nothing is displayed anymore.
Did I make a mistake in the way I'm doing it or am I on the right track?
#pragma once
#include <vector>
#include <fstream>
#include "libs/glm/glm.hpp"
#include "shader.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "mesh.h"
#include <cassert>
class Model {
public:
Model(const char* filename, Shader* shader) {
this->shader = shader;
modelmatrix = glm::mat4(1.0f);
//modelmatrix = glm::scale(modelmatrix, glm::vec3(10));
std::ifstream input = std::ifstream(filename, std::ios::in | std::ios::binary);
uint64 numMeshes;
uint64 numMaterials;
input.read((char*)&numMaterials, sizeof(uint64));
for (uint64 i = 0; i < numMaterials; i++) {
Material material = {};
input.read((char*)&material, sizeof(BMFMaterial));
uint64 diffuseMapNameLenght = 0;
input.read((char*)&diffuseMapNameLenght, sizeof(uint64));
std::string diffuseMapName(diffuseMapNameLenght, '\0');
input.read((char*)&diffuseMapName[0], diffuseMapNameLenght);
uint64 normalMapNameLenght = 0;
input.read((char*)&normalMapNameLenght, sizeof(uint64));
std::string normalMapName(normalMapNameLenght, '\0');
input.read((char*)&normalMapName[0], normalMapNameLenght);
if (diffuseMapNameLenght > 0) {
std::cout << filename << " successfully loaded" << std::endl;
}
else {
std::cout << filename << " failed loading" << std::endl;
}
int32 textureWidth = 0;
int32 textureHeight = 0;
int32 bitsPerPixel = 0;
glGenTextures(2, &material.diffuseMap);
stbi_set_flip_vertically_on_load(true);
auto textureBuffer = stbi_load(diffuseMapName.c_str(), &textureWidth, &textureHeight, &bitsPerPixel, 4); //Map geladen und in Buffer drinnen
glBindTexture(GL_TEXTURE_2D, material.diffuseMap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); //Texture in Grafikkarte geladen
if (textureBuffer) {
stbi_image_free(textureBuffer);
}
auto textureBuffer2 = stbi_load(normalMapName.c_str(), &textureWidth, &textureHeight, &bitsPerPixel, 4); //Map geladen und in Buffer drinnen
glBindTexture(GL_TEXTURE_2D, material.normalMap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer2); //Texture in Grafikkarte geladen
if (textureBuffer2) {
stbi_image_free(textureBuffer2);
}
glBindTexture(GL_TEXTURE_2D, 0);
materials.push_back(material);
}
input.read((char*)&numMeshes, sizeof(uint64));
for (uint64 i = 0; i < numMeshes; i++) {
input.read((char*)&materialIndex, sizeof(uint64));
input.read((char*)&numVertices, sizeof(uint64));
input.read((char*)&numIndices, sizeof(uint64));
for (uint64 i = 0; i < numVertices; i++) {
Vertex vertex;
input.read((char*)&vertex.position.x, sizeof(float));
input.read((char*)&vertex.position.y, sizeof(float));
input.read((char*)&vertex.position.z, sizeof(float));
input.read((char*)&vertex.normal.x, sizeof(float));
input.read((char*)&vertex.normal.y, sizeof(float));
input.read((char*)&vertex.normal.z, sizeof(float));
input.read((char*)&vertex.textureCoord.x, sizeof(float));
input.read((char*)&vertex.textureCoord.y, sizeof(float));
vertices.push_back(vertex);
}
for (uint64 i = 0; i < numIndices; i++) {
uint32 index;
input.read((char*)&index, sizeof(uint32));
indices.push_back(index);
}
Mesh* mesh = new Mesh(vertices, numVertices, indices, numIndices, materials[materialIndex], shader, modelmatrix); //Mesh laden
meshes.push_back(mesh);
}
}
void render() {
for (Mesh* mesh : meshes) {
mesh->render();
}
}
~Model() {
for (Mesh* mesh : meshes) {
delete mesh;
}
}
private:
std::vector<Mesh*> meshes;
std::vector<Material> materials;
glm::mat4 modelmatrix;
std::vector<Vertex> vertices;
uint64 numVertices = 0;
std::vector<uint32> indices;
uint64 numIndices = 0;
uint64 materialIndex = 0;
Shader* shader;
};
#pragma once
#include <iostream>
#include <vector>
#include <fstream>
#include "libs/glm/mat4x4.hpp"
#include "libs/glm/glm.hpp"
#include "shader.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "libs/stb_image.h"
struct BMFMaterial {
glm::vec3 diffuse;
glm::vec3 specular;
glm::vec3 emissive;
float shininess;
};
struct Material {
BMFMaterial material;
GLuint diffuseMap;
GLuint normalMap;
};
class Mesh {
public:
Mesh(std::vector<Vertex>& vertices, uint64 numVertices, std::vector<uint32>&indices, uint64 numIndices, Material material, Shader* shader, glm::mat4 modelmatrix) {
this->material = material;
this->shader = shader;
this->numIndices = numIndices;
this->modelmatrix = modelmatrix;
vertexBuffer = new VertexBuffer(vertices.data(), numVertices);
indexBuffer = new IndexBuffer(indices.data(), numIndices, sizeof(indices[0]));
diffuseLocation = glGetUniformLocation(shader->getShaderID(), "u_material.diffuse");
specularLocation = glGetUniformLocation(shader->getShaderID(), "u_material.specular");
emissiveLocation = glGetUniformLocation(shader->getShaderID(), "u_material.emissive");
shininessLocation = glGetUniformLocation(shader->getShaderID(), "u_material.shininess");
diffuseMapLocation = glGetUniformLocation(shader->getShaderID(), "u_diffuse_map");
modelmatrixlocation = glGetUniformLocation(shader->getShaderID(), "u_modelmatrix");
}
~Mesh() {
delete vertexBuffer;
delete indexBuffer;
}
inline void render() {
std::cout << glm::to_string(modelmatrix) << std::endl;
vertexBuffer->bind();
indexBuffer->bind();
glUniformMatrix4fv(modelmatrixlocation, 1, GL_FALSE, &modelmatrix[0][0]);
glUniform3fv(diffuseLocation, 1, (float*)&material.material.diffuse[0]);
glUniform3fv(specularLocation, 1, (float*)&material.material.specular[0]);
glUniform3fv(emissiveLocation, 1, (float*)&material.material.emissive[0]);
glUniform1f(shininessLocation, material.material.shininess);
glBindTexture(GL_TEXTURE_2D, material.diffuseMap);
glUniform1i(diffuseMapLocation, 0);
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0);
}
private:
VertexBuffer* vertexBuffer;
IndexBuffer* indexBuffer;
Shader* shader;
Material material;
uint64 numIndices = 0;
glm::mat4 modelmatrix;
int diffuseLocation;
int specularLocation;
int emissiveLocation;
int shininessLocation;
int diffuseMapLocation;
int modelmatrixlocation;
};
while (!close) { //GAMELOOP
camera.update();
//model = glm::rotate(model, glm::radians(rotation = 1.0f), glm::vec3(1.0f, 0.0f, 0.0f));
modelViewProj = camera.getViewProj();//*model //Pro Frame jedes mal neu berechnen
glm::mat4 modelView = camera.getView(); //*model
glm::mat4 invModelView = glm::transpose(glm::inverse(modelView));
glm::vec4 sunDirectionCamera = glm::transpose(glm::inverse(camera.getView())) * glm::vec4(sunDirection, 1.0f); //sunDirection ist abhänging von Kamera, weil Licht wird im viewspace berechnet
glUniform3fv(directionLocationDirection, 1, (float*)&sunDirectionCamera[0]); //aktivieren
glm::mat4 pointLightMatrix = glm::mat4(1.0f); //Für Matrixmultiplikation weil der Punkt nicht immer am gleichen Punkt bleiben soll
pointLightPosition = pointLightPosition * pointLightMatrix; //Position ändert sich, rotiert nun um y-Achse
glm::vec3 transformedPointLightPosition = (glm::vec3)(camera.getView() * pointLightPosition); //Transformierte Position im Viewspace
glUniform3fv(positionLocationPoint, 1, (float*)&transformedPointLightPosition[0]);
glUniformMatrix4fv(modelViewProjMatrixLocation, 1, GL_FALSE, &modelViewProj[0][0]); //Matrix setzen/aktivieren
glUniformMatrix4fv(modelViewLocation, 1, GL_FALSE, &modelView[0][0]);
glUniformMatrix4fv(invModelViewLocation, 1, GL_FALSE, &invModelView[0][0]);
//Brett und Figuren rendern/zeichnen
whitepawnA2.render();
board.render();
/*
whitebishopright.render();
whitebishopleft.render();
whiteking.render();
whiteknightleft.render();
whiteknightright.render();
whiterookleft.render();
whiterookright.render();
whitequeen.render();
whitepawnH2.render();
whitepawnG2.render();
whitepawnF2.render();
whitepawnE2.render();
whitepawnD2.render();
whitepawnC2.render();
whitepawnB2.render();
blackknightleft.render();
blackknightright.render();
blackking.render();
blackbishopleft.render();
blackbishopright.render();
blackqueen.render();
blackrookleft.render();
blackrookright.render();
blackpawnA.render();
blackpawnB.render();
blackpawnC.render();
blackpawnD.render();
blackpawnE.render();
blackpawnF.render();
blackpawnG.render();
blackpawnH.render();
*/
SDL_GL_SwapWindow(window); //double buffer aktivieren
//FPS COUNTER
uint64 endCounter = SDL_GetPerformanceCounter();
uint64 counterElapsed = endCounter - lastCounter;
delta = ((float32)counterElapsed) / (float32)perfCounterFrequency; //Zeit die seit letztem Frame vergangen ist
uint32 FPS = (uint32)((float32)perfCounterFrequency / (float32)counterElapsed);
//std::cout << FPS << std::endl;
lastCounter = endCounter;
}
return 0;
}
#include "shader.h"
#include <fstream>
#include <iostream>
Shader::Shader(const char* vertexShaderFilename, const char* fragmentShaderFilename) {
shaderID = createShader(vertexShaderFilename, fragmentShaderFilename);
}
Shader::~Shader() {
glDeleteProgram(shaderID);
}
void Shader::bind() {
glUseProgram(shaderID);
}
void Shader::unbind() {
glUseProgram(0);
}
GLuint Shader::getShaderID() {
return shaderID;
}
GLuint Shader::compile(std::string shaderSource, GLenum type) { //Shadersourcecode so kompiliert werden
GLuint id = glCreateShader(type);
const char* src = shaderSource.c_str();
glShaderSource(id, 1, &src, 0);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result != GL_TRUE) {
int length = 0;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = new char[length];
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Shader compilation error: " << message << std::endl;
delete[] message;
return 0;
}
return id;
}
std::string Shader::parse(const char* filename) {
FILE* file;
#ifdef _WIN32
if (fopen_s(&file, filename, "rb") != 0) {
std::cout << "File " << filename << " not found" << std::endl;
return "";
}
#else
file = fopen(filename, "rb");
if (file == nullptr) {
std::cout << "File " << filename << " not found" << std::endl;
return "";
}
#endif
std::string content; //Dateininhalt
fseek(file, 0, SEEK_END); //ans ende der datei gehen
size_t filesize = ftell(file); //wie weit ist man im file
rewind(file); //wieder zurückgehen
content.resize(filesize); //file resizen damit man nicht zu wenig oder zu viel speicher reserviert
fread(&content[0], 1, filesize, file); //&content[0] gibt adresse auf speicherbereich - 1 ganze filesize soll gelesen werden -
fclose(file); //file wieder schließen
return content; //code returnen
}
GLuint Shader::createShader(const char* vertexShaderFilename, const char* fragmentShaderFilename) {
std::string vertexShaderSource = parse(vertexShaderFilename); //Sourcecode speichern
std::string fragmentShaderSource = parse(fragmentShaderFilename);
GLuint program = glCreateProgram(); //program erstellen
GLuint vs = compile(vertexShaderSource, GL_VERTEX_SHADER); //shader erstellen
GLuint fs = compile(fragmentShaderSource, GL_FRAGMENT_SHADER);
glAttachShader(program, vs); //shader wird program hinzugefügt
glAttachShader(program, fs);
glLinkProgram(program); //executable erstellt und läuft auf vertex
//Solange program gelinked ist, kann alles wieder deattached und gelöscht werden - spart speicherplatz
#ifdef _RELEASE
glDetachShader(progam, vs);
glDetachShader(program, fs);
glDeleteShader(vs);
glDeleteShader(fs);
#endif
return program;
}
//Vertexshader soll Position von Vertex berechnen
#version 330 core
//INPUTS IN DEN SHADER
layout(location = 0) in vec3 a_position; //layout location 0 -> 1 Attribut
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_tex_coord;
//OUTPUTS
out vec3 v_normal;
out vec3 v_position;
out vec2 v_tex_coord;
uniform mat4 u_modelmatrix;
uniform mat4 u_modelView;
uniform mat4 u_modelViewProj;
uniform mat4 u_invModelView;
void main()
{
gl_Position = u_modelmatrix * u_modelViewProj * vec4(a_position, 1.0f); //vec4 für Matrixmultiplikationen - Position in positionsvariable - 1.0f -> 4 Koordinate
v_normal = mat3(u_invModelView) * a_normal; //Normalevktor berechnen
v_position = vec3(u_modelView * vec4(a_position, 1.0f)); //Position des Fragments berechnen
v_tex_coord = a_tex_coord;
}
For any future reader:
A more accurate question would be.
Why did my objects disappear after applying transformation?
After my initial guess - which you can see below - OP posted more detailed code and in the end I found an error in the shader program.
The matrix multiplicaton was in the wrong order:
// OP's original code
gl_Position = u_modelmatrix * u_modelViewProj * vec4(a_position, 1.0f);
// Good order
gl_Position = projection * view * model * vec4(aPos, 1.0);
Reverse order might also be good depending on matrix ordering
Good tutorial about transformations and coordinate systems
Original answer
You didn't provide every part of your code so I can only guess.
I assume in your shader class you create a shader program and then you do the linking correctly.
But in order to update uniforms in a shader, you have to tell that to OpenGL, which shader program to use, by calling:
glUseProgram(shader->getShaderID());
//and then
glUniformMatrix4fv(...);
...
That is usually done in a the render() function (especially crucial if you use multiple shaders) and I can't see that in your code.
Additional code might be needed to find the issue if it's something else (where do you call the render() function and what is happening there? how does you shader program look like?).
Related
I am using the OpenGL v.3.3. and the full source code example from learnopengl.com. I am able to run the textured model from the tutorial (nanosuit.obj), but when i am trying to load another .obj models without textures, but with .mtl files it shows a dark color. I think the problem is maybe in shaders that i am using to load the models, but i dont know how to redo the shader's code to fix my issue.
here is my main functions i am using to render the model:
void COpenGLControl::oglInitialize(void)
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32, // bit depth
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, // z-buffer depth
0, 0, 0, 0, 0, 0, 0,
};
// Get device context only once.
hdc = GetDC()->m_hDC;
// Pixel format.
m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, m_nPixelFormat, &pfd);
// Create the OpenGL Rendering Context.
hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hrc);
if (!gladLoadGL())
{
AfxMessageBox(L"Error loading glad");
return;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
// build and compile shaders
ourShader = Shader("resource/shaders/modelLoading.vs", "resource/shaders/modelLoading.frag");
// load models
ourModel = Model("resource/models/car12/LaFerrari.obj");
// Send draw request
OnDraw(NULL);
}
void COpenGLControl::oglDrawScene(void)
{
// render
// ------
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// don't forget to enable shader before setting uniforms
ourShader.use();
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)900 / (float)900, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
ourShader.setMat4("projection", projection);
ourShader.setMat4("view", view);
// render the loaded model
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
model = glm::scale(model, glm::vec3(0.010f, 0.010f, 0.010f));
model = glm::rotate(model, glm::radians(-30.0f), glm::vec3(0.0f, 1.0f, 0.0f));
model = glm::rotate(model, glm::radians(20.0f), glm::vec3(1.0f, 0.0f, 0.0f));
ourShader.setMat4("model", model);
ourModel.Draw(ourShader);
}
model.cpp
#include "pch.h"
#include "model.h"
Model::Model(void)
{
}
Model::Model(string const& path, bool gamma)
{
loadModel(path);
}
void Model::Draw(Shader shader)
{
for (unsigned int i = 0; i < meshes.size(); i++)
meshes[i].Draw(shader);
}
void Model::loadModel(string const& path)
{
// read file via ASSIMP
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
// check for errors
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
{
cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
return;
}
// retrieve the directory path of the filepath
directory = path.substr(0, path.find_last_of('/'));
// process ASSIMP's root node recursively
processNode(scene->mRootNode, scene);
}
void Model::processNode(aiNode* node, const aiScene* scene)
{
// process each mesh located at the current node
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
// the node object only contains indices to index the actual objects in the scene.
// the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene));
}
// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene);
}
}
Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene)
{
// data to fill
vector<Vertex> vertices;
vector<unsigned int> indices;
vector<Texture> textures;
// Walk through each of the mesh's vertices
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
// positions
if (!mesh->mVertices == NULL)
{
vector.x = mesh->mVertices[i].x;
vector.y = mesh->mVertices[i].y;
vector.z = mesh->mVertices[i].z;
vertex.Position = vector;
}
else
{
vertex.Position = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"Positions is NULL");
}
// normals
if (!mesh->mNormals == NULL)
{
vector.x = mesh->mNormals[i].x;
vector.y = mesh->mNormals[i].y;
vector.z = mesh->mNormals[i].z;
vertex.Normal = vector;
}
else
{
vertex.Normal = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"Normals is NULL");
}
// texture coordinates
if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
{
glm::vec2 vec;
// a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't
// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
vertex.TexCoords = vec;
}
else
{
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
//AfxMessageBox(L"TextCoords is NULL");
}
// tangent
if (!mesh->mTangents == NULL)
{
vector.x = mesh->mTangents[i].x;
vector.y = mesh->mTangents[i].y;
vector.z = mesh->mTangents[i].z;
vertex.Tangent = vector;
}
else
{
vertex.Tangent = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"TextCoords is NULL");
}
// bitangent
if (!mesh->mBitangents == NULL)
{
vector.x = mesh->mBitangents[i].x;
vector.y = mesh->mBitangents[i].y;
vector.z = mesh->mBitangents[i].z;
vertex.Bitangent = vector;
}
else
{
vertex.Bitangent = glm::vec3(0.0f, 0.0f, 0.0f);
//AfxMessageBox(L"Bitangent is NULL");
}
vertices.push_back(vertex);
}
// now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
// retrieve all indices of the face and store them in the indices vector
for (unsigned int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
// process materials
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
aiColor3D color;
Material mat;
// Read mtl file vertex data
material->Get(AI_MATKEY_COLOR_AMBIENT, color);
mat.Ka = glm::vec4(color.r, color.g, color.b, 1.0);
material->Get(AI_MATKEY_COLOR_DIFFUSE, color);
mat.Kd = glm::vec4(color.r, color.g, color.b, 1.0);
material->Get(AI_MATKEY_COLOR_SPECULAR, color);
mat.Ks = glm::vec4(color.r, color.g, color.b, 1.0);
// we assume a convention for sampler names in the shaders. Each diffuse texture should be named
// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER.
// Same applies to other texture as the following list summarizes:
// diffuse: texture_diffuseN
// specular: texture_specularN
// normal: texture_normalN
// 1. diffuse maps
vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// 2. specular maps
vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// 3. normal maps
std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
// 4. height maps
std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());
// return a mesh object created from the extracted mesh data
return Mesh(vertices, indices, textures, mat);
}
vector<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
{
vector<Texture> textures;
for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
// check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
bool skip = false;
for (unsigned int j = 0; j < textures_loaded.size(); j++)
{
if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0)
{
textures.push_back(textures_loaded[j]);
skip = true; // a texture with the same filepath has already been loaded, continue to next one. (optimization)
break;
}
}
if (!skip)
{ // if texture hasn't been loaded already, load it
Texture texture;
texture.id = TextureFromFile(str.C_Str(), this->directory, false);
texture.type = typeName;
texture.path = str.C_Str();
textures.push_back(texture);
textures_loaded.push_back(texture); // store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
}
}
return textures;
}
unsigned int Model::TextureFromFile(const char* path, const string& directory, bool gamma)
{
//string filename = string(path);
//filename = directory + '/' + filename;
string filename = string(path);
if (directory.find(R"(/)") != std::string::npos)
{
filename = directory + R"(/)" + filename;
}
else if (directory.find(R"(\\)") != std::string::npos)
{
filename = directory + R"(\\)" + filename;
}
stbi_set_flip_vertically_on_load(false);
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
Shaders:
frag
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D texture_diffuse;
void main( )
{
color = vec4( texture( texture_diffuse, TexCoords ));
}
vs
#version 330 core
layout ( location = 0 ) in vec3 position;
layout ( location = 1 ) in vec3 normal;
layout ( location = 2 ) in vec2 texCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main( )
{
gl_Position = projection * view * model * vec4( position, 1.0f );
TexCoords = texCoords;
}
Also attached pictures (dark picture is the result of my program, color picture is AbViewer free obj viewer result). Please help me to find a solution, what am i doing wrong?
You need to bind the current texture before your draw-calls. You have no code to set any color information for your vertices for your vertexbuffer.
So without having any lights, no color, and no bonded texture your model will be black :-).
I'm jumping through the hoops right now to learn opengl and I've come across an issue. On my desktop computer with a nvidia gtx 780 opengl is printing out a warning via the glDebugMessageCallback mechanism:
"Buffer object 1 (bound to _GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations."
I'm rendering 1 cube with a vertex and index buffer so this message repeats for every buffer object I'm creating (2 of them). However, there is also one final warning at the end which states:
"Vertex shader in program 3 is being recompiled based on GL State."
I'm still able to render my cube I was rendering before, but the color I had set is flashing between white and the color now. I searched online and found this answer - https://community.khronos.org/t/nvidia-output-debug-error-131185/66033 - which basically said this is nothing but a warning and everything should be fine but that wouldn't explain why my cube is flashing between white and my color now. This same code is working fine on my laptop (2019 Asus laptop which also has a nvidia GTX graphics chip). Anyone come across this issue before? Here is the relevant code:
const char* vertexShaderCode =
R"HereDoc(
#version 430
in layout(location=0) vec3 position;
in layout(location=1) vec3 color;
out vec3 fragColor;
uniform mat4 transformationMatrix;
void main()
{
vec4 newPos = vec4(position, 1.0) * transformationMatrix;//vector is on the left side because my matrices are row major
gl_Position = newPos;
vec3 changedColors;
changedColors.r += color.r + 0;
changedColors.g += color.g + 0;
changedColors.b += color.b + 0;
fragColor = changedColors;
};
)HereDoc";
const char* fragmentShaderCode =
R"HereDoc(
#version 430
out vec4 color;
in vec3 fragColor;
void main()
{
color = vec4(fragColor, 1.0f);
};
)HereDoc";
void GLAPIENTRY MyOpenGLErrorCallbackFunc(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
BGZ_CONSOLE("%s type=0x%x %s\n", ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), type, message);
};
void CheckCompileStatus(GLuint shaderID)
{
GLint compileStatus;
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compileStatus);
if(compileStatus != GL_TRUE)
{
GLint infoLogLength;
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar buffer[512] = {};
GLsizei bufferSize;
glGetShaderInfoLog(shaderID, infoLogLength, &bufferSize, buffer);
BGZ_CONSOLE("%s", buffer);
InvalidCodePath;
};
};
void CheckLinkStatus(GLuint programID)
{
GLint linkStatus;
glGetProgramiv(programID, GL_LINK_STATUS, &linkStatus);
if(linkStatus != GL_TRUE)
{
GLint infoLogLength;
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar buffer[512] = {};
GLsizei bufferSize;
glGetProgramInfoLog(programID, infoLogLength, &bufferSize, buffer);
BGZ_CONSOLE("%s", buffer);
InvalidCodePath;
};
};
local_func void InstallShaders()
{
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
const char* adapter[1];
adapter[0] = vertexShaderCode;
glShaderSource(vertexShaderID, 1, adapter, 0);
adapter[0] = fragmentShaderCode;
glShaderSource(fragmentShaderID, 1, adapter, 0);
glCompileShader(vertexShaderID);
glCompileShader(fragmentShaderID);
CheckCompileStatus(vertexShaderID);
CheckCompileStatus(fragmentShaderID);
GLuint programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
CheckLinkStatus(programID);
glUseProgram(programID);
};
local_func void
GLInit(int windowWidth, int windowHeight)
{
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(MyOpenGLErrorCallbackFunc, 0);
glViewport(0, 0, windowWidth, windowHeight);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);//Defaults to CCW ordering of indicies meaning all indicies that, from the viewers perspective, creating triangles in a CW manner repsrent visible triangles.
glCullFace(GL_BACK);//Culls only back faces (faces facing away from viewer)
InstallShaders();
}
void Draw(Memory_Partition* memPart, s32 id, RunTimeArr<s16> meshIndicies)
{
glDisable(GL_TEXTURE_2D);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 2);
glDrawElements(GL_TRIANGLES, (s32)meshIndicies.length, GL_UNSIGNED_SHORT, 0);
glEnable(GL_TEXTURE_2D);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
};
//This implements a discriminated union for buffering render commands that my game code layer uses.
void RenderViaHardware(Rendering_Info&& renderingInfo, Memory_Partition* platformMemoryPart, int windowWidth, int windowHeight)
{
local_persist bool glIsInitialized { false };
if (NOT glIsInitialized)
{
GLInit(windowWidth, windowHeight);
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
glIsInitialized = true;
};
u8* currentRenderBufferEntry = renderingInfo.cmdBuffer.baseAddress;
Camera3D camera3d = renderingInfo.camera3d;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
for (s32 entryNumber = 0; entryNumber < renderingInfo.cmdBuffer.entryCount; ++entryNumber)
{
RenderEntry_Header* entryHeader = (RenderEntry_Header*)currentRenderBufferEntry;
switch (entryHeader->type)
{
case EntryType_InitBuffer:{
RenderEntry_InitBuffer bufferData = *(RenderEntry_InitBuffer*)currentRenderBufferEntry;
ScopedMemory scope{platformMemoryPart};
RunTimeArr<GLfloat> verts{};
InitArr(verts, platformMemoryPart, bufferData.verts.length * 6);
s32 i{};
f32 colorR{1.0f}, colorG{}, colorB{};//Im just hard coding color data right now while I'm learning
for(s32 j{}; j < bufferData.verts.length; ++j)
{
verts.Push(bufferData.verts[j].x);
verts.Push(bufferData.verts[j].y);
verts.Push(bufferData.verts[j].z);
verts.Push(colorR);
verts.Push(colorG);
verts.Push(colorB);
};
u32 vertexArrayID{};
glGenVertexArrays(1, &vertexArrayID);
glBindVertexArray(vertexArrayID);
GLuint bufferID;
glGenBuffers(1, &bufferID);
glBindBuffer(GL_ARRAY_BUFFER, bufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * verts.length, verts.elements, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (char*)(sizeof(GLfloat)*3));
GLuint indexBufferID;
glGenBuffers(1, &indexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(s16) * bufferData.indicies.length, bufferData.indicies.elements, GL_DYNAMIC_DRAW);
currentRenderBufferEntry += sizeof(RenderEntry_InitBuffer);
}break;
//...other cases for entries which are irrelevant to problem
case EntryType_Geometry: {
ScopedMemory scope{platformMemoryPart};
RenderEntry_Geometry geometryEntry = *(RenderEntry_Geometry*)currentRenderBufferEntry;
//camera transform setup
Mat4x4 xRotMatrix = XRotation(camera3d.rotation.x);
Mat4x4 yRotMatrix = YRotation(camera3d.rotation.y);
Mat4x4 zRotMatrix = ZRotation(camera3d.rotation.z);
Mat4x4 fullRotMatrix = xRotMatrix * yRotMatrix * zRotMatrix;
v3 xAxis = GetColumn(fullRotMatrix, 0);
v3 yAxis = GetColumn(fullRotMatrix, 1);
v3 zAxis = GetColumn(fullRotMatrix, 2);
//Setup full transform matrix
Mat4x4 camTransform = ProduceCameraTransformMatrix(xAxis, yAxis, zAxis, camera3d.worldPos);
Mat4x4 projectionTransform = ProduceProjectionTransformMatrix_UsingFOV(renderingInfo.fov, renderingInfo.aspectRatio, renderingInfo.nearPlane, renderingInfo.farPlane);
Mat4x4 fullTransformMatrix = projectionTransform * camTransform * geometryEntry.worldTransform;
//Send transform matrix to vertex shader
GLint transformMatrixUniformLocation = glGetUniformLocation(3, "transformationMatrix");
glUniformMatrix4fv(transformMatrixUniformLocation, 1, GL_FALSE, &fullTransformMatrix.elem[0][0]);
Draw(platformMemoryPart, geometryEntry.id, geometryEntry.indicies);
currentRenderBufferEntry += sizeof(RenderEntry_Geometry);
}break;
InvalidDefaultCase;
};
}
renderingInfo.cmdBuffer.entryCount = 0;
};
EDIT:
I figured out the issue with the colors not working which was answered in the comments below. However, I still don't know what these warnings are trying to tell me and if they are anything I should be looking to fix.
You variable vec3 changedColors; is uninitialized. Initialize it with vec3 changedColors = vec3(0);. The reason why it works on your laptop might be that its graphics driver will initialize it to zero by default, while your other graphics driver won't.
Regarding the warning (not error). It just warns you that your buffer will be put in video memory since you're using GL_STATIC_DRAW for your buffer. It's actually more of a log and you can safely ignore it. If you want to get rid of it you have to filter it away in your callback (which you passed to glDebugMessageCallback). Your callback will have a severity parameter that lets you to filter messages with a certain severity.
Or, if you only want to get rid of that specific message, filter on its id value.
Here's an example taken from blog.nobel-joergensen.com:
void APIENTRY openglCallbackFunction(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
cout << "---------------------opengl-callback-start------------" << endl;
cout << "message: "<< message << endl;
cout << "type: ";
switch (type) {
case GL_DEBUG_TYPE_ERROR:
cout << "ERROR";
break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
cout << "DEPRECATED_BEHAVIOR";
break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
cout << "UNDEFINED_BEHAVIOR";
break;
case GL_DEBUG_TYPE_PORTABILITY:
cout << "PORTABILITY";
break;
case GL_DEBUG_TYPE_PERFORMANCE:
cout << "PERFORMANCE";
break;
case GL_DEBUG_TYPE_OTHER:
cout << "OTHER";
break;
}
cout << endl;
cout << "id: " << id << endl;
cout << "severity: ";
switch (severity){
case GL_DEBUG_SEVERITY_LOW:
cout << "LOW";
break;
case GL_DEBUG_SEVERITY_MEDIUM:
cout << "MEDIUM";
break;
case GL_DEBUG_SEVERITY_HIGH:
cout << "HIGH";
break;
}
cout << endl;
cout << "---------------------opengl-callback-end--------------" << endl;
}
Following my latest question: My OpenGL QQuickItem won't have the size I want, I'm now trying,, instead of having an arbitrary size for my OpenGL video, to fit it inside my QQuickItem.
I've heard that QQuickItem::transform() will give me a list of transformation matrices. I'm assuming that this list contains all the transformations needed to arrive at a square the size and position of my QQuickItem. <<<< Is this true? If so, the transformations are from what coordinates and viewport dimensions?
Given that as true, I've made the following minimal verifiable and compilable example that tries to make the green screen fit into the size of my QQuickItem. But currently, it just takes the entire screen.
I used the function getModelMatrix() that you can find below, to get all the transformations and make a matrix out of it. Then I apply these transformations to my vertex shader by doing
gl_Position = u_transform * vertexIn;
As I said, the result is a green screen. However, it has the same dimensions of my window, instead of having the dimensions 640x480 and x,y=0, as specified in main.qml
You can find a minimal compilable and verifiable example here: https://github.com/lucaszanella/openglqtquickexample/tree/88fe0092d663dd92c551c72acccd0bf058fe7e5b
OpenGlVideoQtQuick.cpp:
#include "OpenGlVideoQtQuick.h"
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
//Simple shader. Outpus the same location as input, I guess
const char *vString2 = GET_STR(
attribute vec4 vertexIn;
attribute vec2 textureIn;
varying vec2 textureOut;
uniform mat4 u_transform;
void main(void)
{
gl_Position = u_transform * vertexIn;
textureOut = textureIn;
}
);
const char *tString2 = GET_STR(
varying vec2 textureOut;
uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;
void main(void)
{
vec3 yuv;
vec3 rgb;
yuv.x = texture2D(tex_y, textureOut).r;
yuv.y = texture2D(tex_u, textureOut).r - 0.5;
yuv.z = texture2D(tex_v, textureOut).r - 0.5;
rgb = mat3(1.0, 1.0, 1.0,
0.0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0) * yuv;
gl_FragColor = vec4(rgb, 1.0);
}
);
OpenGlVideoQtQuick::OpenGlVideoQtQuick():
openGlVideoQtQuickRenderer(nullptr)
{
connect(this, &QQuickItem::windowChanged, this, &OpenGlVideoQtQuick::handleWindowChanged);
update();
}
void OpenGlVideoQtQuick::handleWindowChanged(QQuickWindow *win)
{
if (win) {
connect(win, &QQuickWindow::beforeSynchronizing, this, &OpenGlVideoQtQuick::sync, Qt::DirectConnection);
win->setClearBeforeRendering(false);
}
}
QMatrix4x4 OpenGlVideoQtQuick::getModelMatrix() {
QMatrix4x4 result;
// Compose model matrix from our transform properties in the QML
QQmlListProperty<QQuickTransform> transformations = transform();
const int count = transformations.count(&transformations);
for (int i=0; i<count; i++) {
QQuickTransform *transform = transformations.at(&transformations, i);
transform->applyTo(&result);
}
return result;
}
void OpenGlVideoQtQuick::update()
{
if (window())
window()->update();
}
OpenGlVideoQtQuickRenderer::~OpenGlVideoQtQuickRenderer()
{
delete program;
}
void OpenGlVideoQtQuick::sync()
{
if (!openGlVideoQtQuickRenderer) {
openGlVideoQtQuickRenderer = new OpenGlVideoQtQuickRenderer();
connect(window(), &QQuickWindow::beforeRendering, openGlVideoQtQuickRenderer, &OpenGlVideoQtQuickRenderer::render, Qt::DirectConnection);
connect(window(), &QQuickWindow::afterRendering, this, &OpenGlVideoQtQuick::update, Qt::DirectConnection);
}
this->openGlVideoQtQuickRenderer->qQuickVideoMatrix = getModelMatrix();
}
static const GLfloat ver[] = {
-1.0f,-1.0f,
1.0f,-1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
static const GLfloat tex[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
void OpenGlVideoQtQuickRenderer::render()
{
if (this->firstRun) {
std::cout << "Creating QOpenGLShaderProgram " << std::endl;
this->firstRun = false;
program = new QOpenGLShaderProgram();
initializeOpenGLFunctions();
std::cout << "Fragment Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Fragment, tString2) << std::endl;
std::cout << "Vertex Shader compilation: " << program->addShaderFromSourceCode(QOpenGLShader::Vertex, vString2) << std::endl;
program->bindAttributeLocation("vertexIn",A_VER);
program->bindAttributeLocation("textureIn",T_VER);
std::cout << "program->link() = " << program->link() << std::endl;
glGenTextures(3, texs);//TODO: ERASE THIS WITH glDeleteTextures
}
program->bind();
program->setUniformValue("u_transform", this->qQuickVideoMatrix);
//glViewport(50, 50, 50, 50);
glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
glEnableVertexAttribArray(A_VER);
glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
glEnableVertexAttribArray(T_VER);
unis[0] = program->uniformLocation("tex_y");
unis[1] = program->uniformLocation("tex_u");
unis[2] = program->uniformLocation("tex_v");
//Y
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//U
glBindTexture(GL_TEXTURE_2D, texs[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
//V
glBindTexture(GL_TEXTURE_2D, texs[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
program->disableAttributeArray(A_VER);
program->disableAttributeArray(T_VER);
program->release();
}
OpenGlVideoQtQuick.h:
#ifndef OpenGlVideoQtQuick_H
#define OpenGlVideoQtQuick_H
#include <QtQuick/QQuickItem>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLFunctions>
#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>
#include <QString>
#include <iostream>
#include <QTimer>
class OpenGlVideoQtQuickRenderer : public QObject, protected QOpenGLFunctions
{
Q_OBJECT
public:
OpenGlVideoQtQuickRenderer() {
}
~OpenGlVideoQtQuickRenderer();
QMatrix4x4 qQuickVideoMatrix;
public slots:
void render();
private:
QOpenGLShaderProgram* program;
GLuint unis[3] = {0};
GLuint texs[3] = {0};
unsigned char *datas[3] = { 0 };
bool firstRun = true;
//TODO: make this variable according to video data
int width = 1920;
int height = 1080;
};
class OpenGlVideoQtQuick : public QQuickItem
{
Q_OBJECT
public:
OpenGlVideoQtQuick();
QMatrix4x4 getModelMatrix();
private slots:
void handleWindowChanged(QQuickWindow *win);
public slots:
void sync();
void update();//Updates the window
private:
OpenGlVideoQtQuickRenderer *openGlVideoQtQuickRenderer;
};
#endif // OpenGlVideoQtQuick_H
main.qml:
import QtQuick 2.0
import OpenGLComponents 1.0
Item {
width: 1280
height: 720
OpenGlVideoQtQuick {
width: 640
height: 480
}
}
From your question and your code it is a little unclear what you were "expecting" to see but I do see a few issues which might help you:
I don't think your render loop is actually called. You must do this in your constructor: setFlag(ItemHasContents);
Make sure you do pass some texture/video data into your glTexImage2D() calls otherwise you will get some garbage (random green garbage?)
You may want to have a look at QQuickItem::widthChanged and QQuickItem::heightChanged signals
Make sure you have that glViewport() as well as glClear() calls when debugging to avoid confusions - you can delete them later when you don't need them anymore
Given that you are outputting Normalized Device coordinates, you should just need to set glViewPort and scissor test to the item's x(), y(), width(), height() (no transform needed on vertices). As far as the green you are seeing, this is because none of your YUV textyres have actual data. As a result, in your pixel shader yuv contains (0, -0.5, -0.5) which when transformed by your YUV to RGB matrix yields rgb(0.0, 0.487625, -1.016055) which is a half intensity green.
I am trying to implement soft particles in my projects.
Everything is fine , I implement the texture also. But when the mouse is moved to a certain angle,
the particles get distorted. The particle is generated in view space.
So, I would like to know how could I implement the billboard in my project so that every particles seem uniform.Here is my code:
bool CETSmokeRenderer::InitBuffers()
{
size_t vertexSize = 3 * 4 * m_NumVertex * sizeof(float);
size_t colorSize = 4 * 4 * m_NumVertex * sizeof(float);
size_t texCoordSize = 2 * 4 * m_NumVertex * sizeof(float);
if(!vertexBuffer)
{
glDeleteBuffersARB(1, &vertexBuffer);
glDeleteBuffersARB(1, &colorBuffer);
glDeleteBuffersARB(1, &texCoordBuffer);
}
glGenBuffersARB(1, &vertexBuffer);
glGenBuffersARB(1, &colorBuffer);
glGenBuffersARB(1, &texCoordBuffer);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexSize, NULL, GL_STREAM_DRAW_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, colorSize, NULL, GL_STREAM_DRAW_ARB);
// Creates the static texture data
size_t len = 2 * 4 * m_NumVertex;
if(0 > m_NumVertex)
{
return false;
}
else if(0 == m_NumVertex)
{
return true;
}
float *texCoords = new float[len];
{
size_t i = 0;
while(i < len)
{
// u v
texCoords[i++] = 0.0f; texCoords[i++] = 0.0f;
texCoords[i++] = 1.0f; texCoords[i++] = 0.0f;
texCoords[i++] = 1.0f; texCoords[i++] = 1.0f;
texCoords[i++] = 0.0f; texCoords[i++] = 1.0f;
}
}
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, texCoordSize, (void*)texCoords, GL_STATIC_DRAW_ARB);
delete texCoords;
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
return 0;
}
void CETSmokeRenderer::Draw(Camera &cam, bool useTex)
{
if(useTex)
glBindTexture(GL_TEXTURE_2D, texID);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
mBaseView->SetupViewingTransform();
size_t len = particleStore.size();
std::vector<SimpleSmokeParticle> toDraw;
for(size_t i = 0; i < len; i++)
{
SimpleSmokeParticle sp;
sp.transP = particleStore[i].p;
sp.index = i;
toDraw.push_back(sp);
}
//std::sort(toDraw.begin(), toDraw.end(), ParticleCmp);
#ifdef USE_VBO
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, 3 * 4 * m_NumVertex * sizeof(float), NULL, GL_STREAM_DRAW_ARB);
float *vertexPtr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
assert(vertexPtr);
for(size_t i = 0, count = 0; count < len; count++)
{
SmokeParticle &prt = particleStore[ toDraw[count].index ];
Point3f &p = toDraw[count].transP;
float w = prt.w / 0.5f;
float h = prt.h / 1.0f;
vertexPtr[i++] = p.x - w; vertexPtr[i++] = p.y - h; vertexPtr[i++] = p.z;
vertexPtr[i++] = p.x + w; vertexPtr[i++] = p.y - h; vertexPtr[i++] = p.z;
vertexPtr[i++] = p.x + w; vertexPtr[i++] = p.y + h; vertexPtr[i++] = p.z;
vertexPtr[i++] = p.x - w; vertexPtr[i++] = p.y + h; vertexPtr[i++] = p.z;
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBuffer);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, 4 * 4 * m_NumVertex * sizeof(float), NULL, GL_STREAM_DRAW_ARB);
float *colorPtr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
assert(colorBuffer);
for(size_t i = 0, count = 0; count < len; count++)
{
SmokeParticle &prt = particleStore[ toDraw[count].index ];
// r g b a
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
colorPtr[i++] = prt.r; colorPtr[i++] = prt.g; colorPtr[i++] = prt.b; colorPtr[i++] = prt.alpha;
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
// Draws buffered data
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, colorBuffer);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texCoordBuffer);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
glDrawArrays(GL_QUADS, 0, (GLsizei)len *4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
#else
{..}
glPopMatrix();
}
void CETSmokeRenderer::Render()
{
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Renders depth information
if(useSoftParticles)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE);
glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE);
glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_FALSE);
glClearColor(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX);
glClear(GL_COLOR_BUFFER_BIT);;
glUseProgramObjectARB(0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_TRUE);
glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_TRUE);
glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_TRUE);
glBindTexture(GL_TEXTURE_2D, 0);
// renders the soft particles
glUseProgramObjectARB(particleShader);
// Sets texture data
GLint texloc = glGetUniformLocationARB(particleShader, "tex");
GLint depthTexloc = glGetUniformLocationARB(particleShader, "depthInfo");
GLint powerloc = glGetUniformLocationARB(particleShader, "power");
glUniform1fARB(powerloc, (float)softParticlePower);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID());
glUniform1iARB(texloc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthTex);
glUniform1iARB(depthTexloc, 1);
Draw(m_pCamera, false);
// Unbinds shader and textures
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgramObjectARB(0);
}
else
{
glUseProgramObjectARB(particleShader);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TextureID());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthTex);
Draw(m_pCamera, true);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgramObjectARB(0);
}
}
One way of achieving what you are looking for is to utilise point sprites, I have attached some code below that illustrates this in a simple way, hope this helps:
main.cpp
/*
Simple point-sprite particle demo - renders particles using spheres
Requirements:
GLM maths library
Freeglut
*/
#include <gl/glew.h>
#include <gl/freeglut.h>
#include <iostream>
#include "GLSLShader.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_projection.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <cassert>
#define GL_CHECK_ERRORS assert(glGetError()== GL_NO_ERROR)
using namespace std;
class Screen
{
public:
int width, height;
string title;
unsigned int displayFlags, contextFlags;
Screen(string ititle, int iwidth = 1024, int iheight = 768){
Screen(ititle, (GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA), (GLUT_CORE_PROFILE | GLUT_DEBUG), iwidth, iheight)
}
Screen(string ititle, unsigned int disFlags, unsigned int contFlags, int iwidth = 1024, int iheight = 768){
title = ititle; width = iwidth; height = iheight;
displayFlags = disFlags;
contextFlags = contFlags;
}
};
const int TOTAL= 9;
GLfloat positions[3*TOTAL]={-1,0,-1, 0,0,-1, 1,0,-1,-1,0, 0, 0,0, 0, 1,0, 0,-1,0, 1, 0,0, 1, 1,0,1};
GLuint vboID, vaoID;
GLsizei stride = sizeof(GLfloat)*3;
GLSLShader shader;
int filling=1;
// Absolute rotation values (0-359 degrees) and rotiation increments for each frame
float rotation_x=0, rotation_x_increment=0.1f;
float rotation_y=0, rotation_y_increment=0.05f;
float rotation_z=0, rotation_z_increment=0.03f;
glm::mat4 P; //projection matrix;
bool bRotate=true;
void InitShaders(void)
{
shader.LoadFromFile(GL_VERTEX_SHADER, "shader.vert");
shader.LoadFromFile(GL_FRAGMENT_SHADER, "shader.frag");
shader.CreateAndLinkProgram();
shader.Use();
shader.AddAttribute("vVertex");
shader.AddUniform("Color");
shader.AddUniform("lightDir");
shader.AddUniform("MVP");
glUniform3f(shader("lightDir"), 0,0,1);
glUniform3f(shader("Color"),1,0,0);
shader.UnUse();
GL_CHECK_ERRORS;
}
void InitVAO() {
GL_CHECK_ERRORS;
//Create vao and vbo stuff
glGenVertexArrays(1, &vaoID);
glGenBuffers (1, &vboID);
GL_CHECK_ERRORS;
glBindVertexArray(vaoID);
glBindBuffer (GL_ARRAY_BUFFER, vboID);
glBufferData (GL_ARRAY_BUFFER, sizeof(positions), &positions[0], GL_STATIC_DRAW);
GL_CHECK_ERRORS;
glEnableVertexAttribArray(shader["vVertex"]);
glVertexAttribPointer (shader["vVertex"], 3, GL_FLOAT, GL_FALSE,stride,0);
glBindVertexArray(0);
GL_CHECK_ERRORS;
}
void SetupGLBase() {
glGetError();
GL_CHECK_ERRORS;
glClearColor(0.0f,0.0f,0.2f,0.0f);
GL_CHECK_ERRORS;
InitShaders();
InitVAO();
glEnable(GL_DEPTH_TEST); // We enable the depth test (also called z buffer)
GL_CHECK_ERRORS;
glPointSize(50);
}
void OnRender() {
GL_CHECK_ERRORS;
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
//setup matrices
glm::mat4 T = glm::translate(glm::mat4(1.0f),glm::vec3(0.0f, 0.0f, -5));
glm::mat4 Rx = glm::rotate(T, rotation_x, glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 Ry = glm::rotate(Rx, rotation_y, glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 MV = glm::rotate(Ry, rotation_z, glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 MVP = P*MV;
//draw the points
shader.Use();
glUniformMatrix4fv(shader("MVP"), 1, GL_FALSE, glm::value_ptr(MVP));
glBindVertexArray(vaoID);
glDrawArrays(GL_POINTS, 0, TOTAL);
glBindVertexArray(0);
shader.UnUse();
glutSwapBuffers();
}
void OnResize(int w, int h)
{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
//setup the projection matrix
P = glm::perspective(45.0f, (GLfloat)w/h, 1.f, 1000.f);
}
void OnShutdown() {
glDeleteBuffers(1, &vboID);
glDeleteVertexArrays(1, &vaoID);
}
void OnKey(unsigned char key, int x, int y)
{
switch (key)
{
case ' ': bRotate=!bRotate; break;
case 'r': case 'R':
if (filling==0)
{
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); // Filled Polygon Mode
filling=1;
}
else
{
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); // Outline Polygon Mode
filling=0;
}
break;
}
}
void OnSpecialKey(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_UP: rotation_x_increment = rotation_x_increment +0.005f; break;
case GLUT_KEY_DOWN: rotation_x_increment = rotation_x_increment -0.005f; break;
case GLUT_KEY_LEFT: rotation_y_increment = rotation_y_increment +0.005f; break;
case GLUT_KEY_RIGHT: rotation_y_increment = rotation_y_increment -0.005f; break;
}
}
void OnIdle() {
if(bRotate) {
rotation_x = rotation_x + rotation_x_increment;
rotation_y = rotation_y + rotation_y_increment;
rotation_z = rotation_z + rotation_z_increment;
}
if (rotation_x > 359) rotation_x = 0;
if (rotation_y > 359) rotation_y = 0;
if (rotation_z > 359) rotation_z = 0;
glutPostRedisplay();
}
void glTestAndInfo(GLEnum glewInitResponse)
{
if (GLEW_OK != glewInitResponse) {
cerr<<"Error: "<<glewGetErrorString(glewInitResponse)<<endl;
} else {
if (GLEW_VERSION_3_3)
{
cout<<"Driver supports OpenGL 3.3 or greater.\nDetails:"<<endl;
}
}
cout<<"Using GLEW "<<glewGetString(GLEW_VERSION)<<endl;
cout<<"Vendor: "<<glGetString (GL_VENDOR)<<endl;
cout<<"Renderer: "<<glGetString (GL_RENDERER)<<endl;
cout<<"Version: "<<glGetString (GL_VERSION)<<endl;
cout<<"GLSL: "<<glGetString (GL_SHADING_LANGUAGE_VERSION)<<endl;
}
void main(int argc, char** argv) {
Screen *screen = news Screen("Point sprites as spheres in OpenGL 3.3");
atexit(OnShutdown);
glutInit(&argc, argv);
glutInitDisplayMode(screen->displayFlags);
glutInitContextVersion (3, 3);
glutInitContextFlags (screen->contextFlags);
glutInitWindowSize(screen->width, screen->height);
glutCreateWindow(screen->title);
glewExperimental = GL_TRUE;
glTestAndInfo(glewInit());
SetupGLBase();
glutDisplayFunc(OnRender);
glutReshapeFunc(OnResize);
glutKeyboardFunc(OnKey);
glutSpecialFunc(OnSpecialKey);
glutIdleFunc(OnIdle);
glutMainLoop();
}
GLSLShader.h
#pragma once
#ifndef GLSL_SHADER_H
#define GLSL_SHADER_H
#include <GL/glew.h>
#include <map>
#include <string>
using namespace std;
class GLSLShader
{
public:
GLSLShader(void);
~GLSLShader(void);
void LoadFromString(GLenum whichShader, const string source);
void LoadFromFile(GLenum whichShader, const string filename);
void CreateAndLinkProgram();
void Use();
void UnUse();
void AddAttribute(const string attribute);
void AddUniform(const string uniform);
GLuint operator[](const string attribute);// indexer: returns the location of the named attribute
GLuint operator()(const string uniform);
private:
enum ShaderType {VERTEX_SHADER, FRAGMENT_SHADER, GEOMETRY_SHADER};
GLuint _program;
int _totalShaders;
GLuint _shaders[3];//0 vertexshader, 1 fragmentshader, 2 geometryshader
map<string,GLuint> _attributeList;
map<string,GLuint> _uniformLocationList;
};
#endif
GLSLShader.cpp
/*
Really basic glsl shader class
*/
#include "GLSLShader.h"
#include <iostream>
#include <fstream>
// constructor
GLSLShader::GLSLShader(void)
{
_totalShaders=0;
_shaders[VERTEX_SHADER]=0;
_shaders[FRAGMENT_SHADER]=0;
_shaders[GEOMETRY_SHADER]=0;
_attributeList.clear();
_uniformLocationList.clear();
}
// destructor
GLSLShader::~GLSLShader(void)
{
_attributeList.clear();
_uniformLocationList.clear();
glDeleteProgram(_program);
}
// loader functions
void GLSLShader::LoadFromString(GLenum type, const string source) {
GLuint shader = glCreateShader (type);
const char * ptmp = source.c_str();
glShaderSource (shader, 1, &ptmp, NULL);
//check whether the shader loads fine
GLint status;
glCompileShader (shader);
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLint infoLogLength;
glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *infoLog= new GLchar[infoLogLength];
glGetShaderInfoLog (shader, infoLogLength, NULL, infoLog);
cerr<<"Compile log: "<<infoLog<<endl;
delete [] infoLog;
}
_shaders[_totalShaders++]=shader;
}
void GLSLShader::LoadFromFile(GLenum whichShader, const string filename){
ifstream fp;
fp.open(filename.c_str(), ios_base::in);
if(fp) {
string line, buffer;
while(getline(fp, line)) {
buffer.append(line);
buffer.append("\r\n");
}
//copy to source
LoadFromString(whichShader, buffer);
} else {
cerr<<"Error loading shader: "<<filename<<endl;
}
}
// utilitarian functions
void GLSLShader::CreateAndLinkProgram() {
_program = glCreateProgram ();
if (_shaders[VERTEX_SHADER] != 0) {
glAttachShader (_program, _shaders[VERTEX_SHADER]);
}
if (_shaders[FRAGMENT_SHADER] != 0) {
glAttachShader (_program, _shaders[FRAGMENT_SHADER]);
}
if (_shaders[GEOMETRY_SHADER] != 0) {
glAttachShader (_program, _shaders[GEOMETRY_SHADER]);
}
//link and check whether the program links fine
GLint status;
glLinkProgram (_program);
glGetProgramiv (_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLint infoLogLength;
glGetProgramiv (_program, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *infoLog= new GLchar[infoLogLength];
glGetProgramInfoLog (_program, infoLogLength, NULL, infoLog);
cerr<<"Link log: "<<infoLog<<endl;
delete [] infoLog;
}
glDeleteShader(_shaders[VERTEX_SHADER]);
glDeleteShader(_shaders[FRAGMENT_SHADER]);
glDeleteShader(_shaders[GEOMETRY_SHADER]);
}
void GLSLShader::Use() {
glUseProgram(_program);
}
void GLSLShader::UnUse() {
glUseProgram(0);
}
void GLSLShader::AddAttribute(const string attribute) {
_attributeList[attribute]= glGetAttribLocation(_program, attribute.c_str());
}
// indexer: returns the location of the named attribute
GLuint GLSLShader::operator [](const string attribute) {
return _attributeList[attribute];
}
void GLSLShader::AddUniform(const string uniform) {
_uniformLocationList[uniform] = glGetUniformLocation(_program, uniform.c_str());
}
GLuint GLSLShader::operator()(const string uniform){
return _uniformLocationList[uniform];
}
This code is pretty old and I have no way to test rendering here (no distinct GFX card) so if there are any issues let me know and I can fix it once at my GFX dev machine.
Addendum:
Shaders may help too (dont know how I forgot them, old age maybe catching up on me!) so here they are:
Vertex shader (shader.vert)
#version 330 // set this to whatever minimum version you want to support
in vec3 vVertex;
uniform mat4 MVP;
void main()
{
gl_Position = MVP*vec4(vVertex,1);
}
Fragment shader (shader.frag)
#version 330
out vec4 vFragColour;
uniform vec3 Colour;
uniform vec3 lightDirection;
void main(void)
{
// calculate normal from texture coordinates
vec3 N;
N.xy = gl_PointCoord* 2.0 - vec2(1.0);
float mag = dot(N.xy, N.xy);
if (mag > 1.0) discard; // kill pixels outside the circle we want
N.z = sqrt(1.0-mag); // this might be expensive depending on your hardware
float diffuse = max(0.0, dot(lightDirection, N)); // calculate lighting
vFragColour = vec4(Colour,1) * diffuse;
}
Addendum 2:
To add the freeglut libraries to your build and resolve LNK 1104 errors simply go to *Project >> Properties >> VC++ Directories* and add the directories where your freeglut includes, source libraries and dlls are stored, for example for lib files go to
Add the folders as follows:
DLL Directories: add to Executable Directories
.h file Directories(include folder): add to Include Directories
.cpp file Directories: add to Source Directories
.lib file Directories: add to Library Directories
Hope this helps:)
I'm using VC++10 + OpenGL + the Assimp Library to consume and then render some 3D models.
The code is rendering the positions correctly, but for some reason the textures are seriously bugged. My texcoords appear to be loading correctly as are the texture files themselves - however I can't help but feel that the issue must be located with the loaded textures themselves.
www.flickr.com/photos/95269725#N02/8685913640/in/photostream
{ i seem to have a lack of rep to post inline images }
********** EDIT1 : ***********
So, I've been using the awesome GDebugger application to debug and interrogate the OpenGL pipeline in realtime. 2 things stand out really :
1. The biggy here is that the loaded texture is meant to look like this ->
http://www.flickr.com/photos/95269725#N02/8688860034/in/photostream
but actually looks like this when loaded into OpenGL memory :
http://www.flickr.com/photos/95269725#N02/8688860042/in/photostream/
2. Not sure if this is still applicable(as discussed in the comments), however the GL_TEXTURE_2D state variable is always FALSE throughout the game loop.
So I'm going to have to play with the texture loading code to see if I can get any traction there and post another update.
A few big relevant code chunks{sorry!} :
* Vertex Shader *
#version 420
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;
uniform mat4 cameraToClipMatrix;
uniform mat4 modelToCameraMatrix;
out vec2 oTexCoord;
out vec4 oNormal;
void main()
{
oTexCoord = texCoord;
vec4 cameraPos = modelToCameraMatrix * vec4(position,1.0);
gl_Position = cameraToClipMatrix * cameraPos;
oNormal = normalize(vec4(modelToCameraMatrix * vec4(normal,0.0)));
}
* Fragment Shader *
#version 420
in vec4 Normal;
in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
uniform sampler2D gSampler;
void main()
{
FragColor = texture(gSampler, TexCoord);
//FragColor = vec4(1.1, 0.0, 1.1, 1.0);
}
* GL Init etc *
void GLSystem::init() {
InitializeProgram();
glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glDepthRange(0.0f, 1.0f);
}
void GLSystem::InitializeProgram()
{
std::vector<GLuint> shaderList;
shaderList.push_back(LoadShader(GL_VERTEX_SHADER, "VertShader1.vert"));
shaderList.push_back(LoadShader(GL_FRAGMENT_SHADER, "FragShader1.frag"));
theProgram = CreateProgram(shaderList);
modelToCameraMatrixUnif = glGetUniformLocation(theProgram, "modelToCameraMatrix"); // view matrix
cameraToClipMatrixUnif = glGetUniformLocation(theProgram, "cameraToClipMatrix"); // projection matrix
m_samplerUnif = glGetUniformLocation(theProgram, "gSampler"); // grab the gSampler uniform location reference in the fragment shader
float fzNear = 1.0f; float fzFar = 45.0f;
cameraToClipMatrix[0].x = fFrustumScale;
cameraToClipMatrix[1].y = fFrustumScale;
cameraToClipMatrix[2].z = (fzFar + fzNear) / (fzNear - fzFar);
cameraToClipMatrix[2].w = -1.0f;
cameraToClipMatrix[3].z = (2 * fzFar * fzNear) / (fzNear - fzFar);
glUseProgram(theProgram);
glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_FALSE, glm::value_ptr(cameraToClipMatrix));
glUseProgram(0);
}
* Texture Loading *
bool CTexture::Load() {
m_texObj = 0; // init to zero
std::auto_ptr<glimg::ImageSet> pImgSet;
try {
pImgSet.reset( glimg::loaders::stb::LoadFromFile(m_filename) );
m_texObj = glimg::CreateTexture( &(*pImgSet), 0); // generates a texture and returns the related texture id
//glimg::SingleImage image = pImgSet->GetImage(0, 0, 0);
//glimg::Dimensions dims = image.GetDimensions();
//GLuint targetTexType = glimg::GetTextureType( &(*pImgSet), 0); // not using this yet - but potentially might need to base this objects targetType on this interpreted value.
//glimg::OpenGLPixelTransferParams params = GetUploadFormatType(image.GetFormat(), 0);
//glPixelStorei(GL_UNPACK_ALIGNMENT, image.GetFormat().LineAlign());
//glGenTextures(1, &m_texObj);
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, m_texObj);
//glTexImage2D(m_targetType, 0, glimg::GetInternalFormat(image.GetFormat(), 0), dims.width, dims.height, 0, params.format, params.type, image.GetImageData());
//glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, dims.width, dims.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.GetImageData() );
/*glTexParameterf(m_targetType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(m_targetType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(m_targetType, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(m_targetType, GL_TEXTURE_WRAP_T, GL_REPEAT);*/
}
catch(glimg::loaders::stb::StbLoaderException &e) {
std::cout << "Warning : " << e.what() << " || .Image file loading failed for file : '" << m_filename << std::endl;
return false;
}
glBindTexture(m_targetType, 0); // Bind to default texture
return true;
}
* Mesh Loading *
#include "MeshModel.h"
// ----------------------------------------------------------------------------------------
#include "Texture.h"
#include "GLSystem.h"
#include "Game.h"
// ----------------------------------------------------------------------------------------
#include <assert.h>
// ----------------------------------------------------------------------------------------
MeshItem::MeshItem() {
}
MeshItem::MeshItem(MeshModel& p_meshModel) {
m_pmeshModel = &p_meshModel;
p_delete_object_data = true;
VBO = INVALID_OGL_VALUE;
IBO = INVALID_OGL_VALUE;
NBO = INVALID_OGL_VALUE;
TBO = INVALID_OGL_VALUE;
NumVertices = 0;
NumFaces = 0;
NumIndices = 0;
MaterialIndex = INVALID_MATERIAL;
};
MeshItem::~MeshItem() {
if (VBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &VBO);
}
if (IBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &IBO);
}
if (NBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &NBO);
}
if (TBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &TBO);
}
}
void MeshItem::BuildVBO() {
glGenVertexArrays(1, &VAO); /* Generate a vertex array object - container for all vertex attribute arrays */
glBindVertexArray(VAO); /* Bind this VAO as the current Vertex Attribute Array container [ Holds the state for all attributes i.e. not the Vertex and Index data ] */
// Positions
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 3, &Positions[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Positions
// Indices
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * NumFaces * 3, &Indices[0], GL_STATIC_DRAW);
// Normals
glGenBuffers(1, &NBO);
glBindBuffer(GL_ARRAY_BUFFER, NBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 3, &Normals[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); // Normals
// TexCoords
glGenBuffers(1, &TBO);
glBindBuffer(GL_ARRAY_BUFFER, TBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 2, &TexCoords[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0); // TexCoords
glBindVertexArray(0); // Unbind the VAO
glBindBuffer(GL_ARRAY_BUFFER,0); // Unbind the vertices array buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind the indices array buffer
// Our copy of the data is no longer necessary, it is safe in the graphics card memory
if(p_delete_object_data) {
Positions.erase( Positions.begin(), Positions.end() );
Indices.erase( Indices.begin(), Indices.end() );
Normals.erase( Normals.begin(), Normals.end() );
TexCoords.erase( TexCoords.begin(), TexCoords.end() );
}
}
// ********************* MESHMODEL *********************
MeshModel::MeshModel(GLSystem& p_gls)
: m_pgls(&p_gls)
{
m_texUnit = 0;
m_samplerObj = 0;
}
MeshModel::~MeshModel() {
Clear();
}
GLSystem& MeshModel::getGLSystem() {
return *m_pgls;
}
void MeshModel::Clear() {
//for (unsigned int i = 0 ; i < m_textures.size() ; i++) {
// m_textures[i]);
//}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
bool MeshModel::LoadMesh(const std::string& p_filename) {
Clear(); // Release the previously loaded mesh (if it exists)
bool Ret = false;
Assimp::Importer Importer;
const aiScene* pScene = Importer.ReadFile(p_filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals /* | aiProcess_FlipWindingOrder*/ /* | aiProcess_FlipUVs*/ | aiProcess_ValidateDataStructure);
//const aiScene* pScene = aiImportFile(p_filename.c_str(), aiProcessPreset_TargetRealtime_MaxQuality);
if (pScene) {
printf("3D Object File '%s' loaded successfully.\n", p_filename.c_str() );
Ret = InitFromScene(pScene, p_filename);
}
else {
printf("Error parsing '%s': '%s'.\n", p_filename.c_str(), Importer.GetErrorString());
}
return Ret;
}
bool MeshModel::InitFromScene(const aiScene* pScene, const std::string& p_filename) {
//m_meshItems.resize(pScene->mNumMeshes);
m_textures.resize(pScene->mNumMaterials);
InitMaterials(pScene, p_filename); // load materials/textures etc
// Initialize the meshes in the scene one by one
for (unsigned int i = 0 ; i < pScene->mNumMeshes ; i++) {
const aiMesh* paiMesh = pScene->mMeshes[i];
MeshItem mItem(*this);
InitMesh(mItem, paiMesh);
mItem.BuildVBO();
m_meshItems.push_back(mItem);
}
return true;
}
void MeshModel::InitMesh(MeshItem& p_meshItem, const aiMesh* p_paiMesh) {
p_meshItem.MaterialIndex = p_paiMesh->mMaterialIndex;
// Indices
p_meshItem.NumFaces = p_paiMesh->mNumFaces;
p_meshItem.NumIndices = p_meshItem.NumFaces * 3;
p_meshItem.Indices.resize(p_meshItem.NumIndices);
for (unsigned int i = 0 ; i < p_paiMesh->mNumFaces ; ++i) {
const aiFace& face = p_paiMesh->mFaces[i];
assert(face.mNumIndices == 3);
p_meshItem.Indices[i*3+0] = face.mIndices[0];
p_meshItem.Indices[i*3+1] = face.mIndices[1];
p_meshItem.Indices[i*3+2] = face.mIndices[2];
}
p_meshItem.NumVertices = p_paiMesh->mNumVertices;
p_meshItem.Positions.resize(p_meshItem.NumVertices * 3);
p_meshItem.Normals.resize(p_meshItem.NumVertices * 3);
p_meshItem.TexCoords.resize(p_meshItem.NumVertices * 2);
for (unsigned int i = 0 ; i < p_paiMesh->mNumVertices ; ++i) {
// Positions
if( p_paiMesh->HasPositions() ) {
p_meshItem.Positions[i*3+0] = p_paiMesh->mVertices[i].x;
p_meshItem.Positions[i*3+1] = p_paiMesh->mVertices[i].y;
p_meshItem.Positions[i*3+2] = p_paiMesh->mVertices[i].z;
}
// Normals
if( p_paiMesh->HasNormals() ) {
p_meshItem.Normals[i*3+0] = p_paiMesh->mNormals[i].x;
p_meshItem.Normals[i*3+1] = p_paiMesh->mNormals[i].y;
p_meshItem.Normals[i*3+2] = p_paiMesh->mNormals[i].z;
}
// TexCoords
if( p_paiMesh->HasTextureCoords(0) ) {
p_meshItem.TexCoords[i*2+0] = p_paiMesh->mTextureCoords[0][i].x;
p_meshItem.TexCoords[i*2+1] = p_paiMesh->mTextureCoords[0][i].y;
}
}
}
bool MeshModel::InitMaterials(const aiScene* pScene, const std::string& p_filename) {
// Extract the directory part from the file name
std::string::size_type SlashIndex = p_filename.find_last_of("/");
std::string Dir;
if (SlashIndex == std::string::npos) {
Dir = ".";
}
else if (SlashIndex == 0) {
Dir = "/";
}
else {
Dir = p_filename.substr(0, SlashIndex);
}
bool Ret = true;
// Initialize the materials
for (unsigned int i = 0 ; i < pScene->mNumMaterials ; i++) {
const aiMaterial* pMaterial = pScene->mMaterials[i];
m_textures[i] = NULL;
std::string FullPath = "";
if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString Path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
FullPath = Dir + "/" + Path.data;
m_textures[i] = std::make_shared<CTexture>( GL_TEXTURE_2D, FullPath.c_str() );
if ( !m_textures[i]->Load() ) {
printf("Error loading texture '%s'.\n", FullPath.c_str());
m_textures[i].reset();
m_textures[i] = NULL;
Ret = false;
}
else {
printf("Texture File '%s' loaded successfully\n", FullPath.c_str());
}
}
}
// Load a white texture in case the model does not include its own texture
if (!m_textures[i]) {
m_textures[i] = std::make_shared<CTexture>( GL_TEXTURE_2D, "..//Data/Textures/white.png");
printf("A default Texture File was loaded for '%s'.\n", FullPath.c_str());
Ret = m_textures[i]->Load();
}
}
// Genertate a Sampler object
glGenSamplers(1, &m_samplerObj);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return Ret;
}
void MeshModel::DrawMesh() {
for (unsigned int i = 0 ; i < m_meshItems.size() ; i++) {
glUseProgram( getGLSystem().getProgram() ); // Bind to our selected shader program
glBindVertexArray(m_meshItems[i].VAO);
const unsigned int MaterialIndex = m_meshItems[i].MaterialIndex;
// If textures exist then bind them to samplers etc
if (MaterialIndex < m_textures.size() && m_textures[MaterialIndex]) {
glUniform1i(m_pgls->m_samplerUnif, 0);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, m_textures[MaterialIndex]->m_texObj);
glBindSampler(0, m_samplerObj);
} else {
printf("MeshItem has no material!");
}
// RTS
glutil::MatrixStack currMatrix;
currMatrix.Translate(glm::vec3(0.0f, -3.0f, -10.0f));
currMatrix.Scale(0.1f, 0.1f, 0.1f);
currMatrix.RotateX(-90);
float a = Game::m_tick.asSeconds() /10;
float fAngRad = m_pgls->ComputeAngleRad(a, 2.0);
float fCos = cosf(fAngRad);
float fSin = sinf(fAngRad);
glm::mat3 theMat(1.0f);
theMat[0].x = fCos; theMat[1].x = -fSin;
theMat[0].y = fSin; theMat[1].y = fCos;
currMatrix.ApplyMatrix(glm::mat4(theMat));
glUniformMatrix4fv(m_pgls->modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));
glDrawElements(GL_TRIANGLES, m_meshItems[i].NumIndices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); // Unbind the VAO
glUseProgram(0); // Close the link to the bound shader programs
}
}
I notice your vertex shader declares:
out vec2 oTexCoord;
but your fragment shader declares:
in vec2 TexCoord;
This might leave your texture coordinates undefined.
I think you need to enable textures with glEnable(GL_TEXTURES_2D) in your init section. I get the same look by commenting out that line from my project. Here's the code, if that helps:
EnableGraphics::EnableGraphics()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // typical alpha transparency
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
EDIT:
In case your link dies, I should add that your screenshot shows a 3D model with no textures or shading, although it has colors.