I write a program to implement environment mapping using OpenGL and Cg shader language.But the result is not very right.When calculate the color of the model,we will blend the reflection with a decal texture.A uniform parameter called reflectivity allows the application to control how reflective the material is.
Firstly I list my fragment Cg code:
void main_f(float2 texCoord : TEXCOORD0,
float3 R : TEXCOORD1,
out float4 color : COLOR,
uniform float reflectivity,
uniform sampler2D decalMap,
uniform samplerCUBE environmentMap)
{
//fetch reflected environment color
float3 reflectedColor = texCUBE(environmentMap,R);
//fetch the decal base coloe
float3 decalColor = tex2D(decalMap,texCoord);
color.xyz = lerp(reflectedColor,decalColor,reflectivity);//change !!!!!!!!
color.w = 1;
}
I set the uniform parameter reflectivity as 0.6.And the result is :
As we can see,the color information from the decal texture is lost.There is only color information from environment cube texture.And if I set reflectivity as 0,the model will be dark.
But if I change the color.xyz in the fragment cg code as :
color.xyz = decalColor;
I can get the right result(only has color from decal texture) :
And if I change the color.xyz in the fragment cg code as :
color.xyz = reflectedColor;
I can get the right result(only has color from environment cube texture) ,too:
And my question is :
Why it does not work when I blend the color information from decal texture with the color information from environment cube texture using Cg function lerp?
at last I list my cg vertex shader and cpp file:
vertex.cg:
void main_v(float4 position : POSITION,
float2 texCoord : TEXCOORD0,//decal texture
float3 normal : NORMAL,
out float4 oPosition : POSITION,
out float2 oTexCoord : TEXCOORD0,//out decal texture
out float3 R : TEXCOORD1,//reflective vector
uniform float3 eyePositionW,//eye position in world space
uniform float4x4 modelViewProj,
uniform float4x4 modelToWorld
)
{
modelViewProj = glstate.matrix.mvp;
oPosition = mul(modelViewProj,position);
oTexCoord = texCoord;
float3 positionW = mul(modelToWorld,position).xyz;
float3 N = mul((float3x3)modelToWorld,normal);
N = normalize(N);
float3 I = positionW - eyePositionW;//incident vector
R = reflect(I,N);
}
main.cpp:
#pragma comment(lib,"glew32.lib")
#pragma comment(lib,"GLAUX.LIB")
#pragma comment(lib,"cg.lib")
#pragma comment(lib,"cgGL.lib")
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/glaux.h>
#include <CG/cg.h>
#include <CG/cgGL.h>
#include "MonkeyHead.h"
#include <iostream>
#include <cmath>
using namespace std;
int loop;
/* Use enum to assign unique symbolic OpenGL texture names. */
enum {
TO_BOGUS = 0,
TO_DECAL,
TO_ENVIRONMENT,
};
const double myPi = 3.14159;
//for Cg shader
static CGcontext myCgContext;
static CGprofile myCgVertexProfile,myCgFragmentProfile;
static CGprogram myCgVertexProgram,myCgFragmentProgram;
static const char *myProgramName = "CgTest18CubeMapReflective",
*myVertexProgramFileName = "vertex.cg",
*myVertexProgramName = "main_v",
*myFragmentProgramFileName = "fragment.cg",
*myFragmentProgramName = "main_f";
static CGparameter myCgVertexParam_modelToWorld;
//bmp files for cube map
const char *bmpFile[6] = {"Data/1.bmp","Data/2.bmp","Data/3.bmp",
"Data/4.bmp","Data/5.bmp","Data/6.bmp"};
const char *decalBmpFile = "Data/decal.bmp";
static float eyeAngle = 0.53;
static float eyeHeight = 0.0f;
static float headSpain = 0.0f;
static const GLfloat vertex[4*6][3] = {
/* Positive X face. */
{ 1, -1, -1 }, { 1, 1, -1 }, { 1, 1, 1 }, { 1, -1, 1 },
/* Negative X face. */
{ -1, -1, -1 }, { -1, 1, -1 }, { -1, 1, 1 }, { -1, -1, 1 },
/* Positive Y face. */
{ -1, 1, -1 }, { 1, 1, -1 }, { 1, 1, 1 }, { -1, 1, 1 },
/* Negative Y face. */
{ -1, -1, -1 }, { 1, -1, -1 }, { 1, -1, 1 }, { -1, -1, 1 },
/* Positive Z face. */
{ -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 },
/* Negative Z face. */
{ -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 },
};
static float reflectivity = 0.6;
GLuint decalTexture;
bool animating = false;//enable animating or not
static void drawMonkeyHead()
{
static GLfloat *texCoords = NULL;
const int numVertices = sizeof(MonkeyHead_vertices)
/ (3 * sizeof(MonkeyHead_vertices[0]));
const float scaleFactor = 1.5;
//generate texcoords
texCoords = (GLfloat*)malloc(2 * numVertices * sizeof(GLfloat));
if (!texCoords)
{
cerr << "ERROR : Monkey head texcoords memory malloc failed !" << endl;
exit(1);
}
for (loop = 0;loop < numVertices;++loop)
{
texCoords[loop * 2] = scaleFactor * MonkeyHead_vertices[3 * loop];
texCoords[loop * 2 + 1] = scaleFactor * MonkeyHead_vertices[3 * loop + 1];
}
//use vertex array
//enable array
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//assign array data
glVertexPointer(3,GL_FLOAT,3 * sizeof(GLfloat),MonkeyHead_vertices);
glNormalPointer(GL_FLOAT,3 * sizeof(GLfloat),MonkeyHead_normals);
glTexCoordPointer(2,GL_FLOAT,2 * sizeof(GLfloat),texCoords);
glDrawElements(GL_TRIANGLES,3 * MonkeyHead_num_of_triangles,
GL_UNSIGNED_SHORT,MonkeyHead_triangles);
}
//read bmp image file
AUX_RGBImageRec *LoadBMP(const char *FileName)
{
FILE *File = NULL;
if(!FileName)
return NULL;
File = fopen(FileName,"r");
if (File)
{
fclose(File);
return auxDIBImageLoad(FileName);
}
return NULL;
}
//load decal texture from a bmp file
int loadDecalTexture()
{
int status = 1;
AUX_RGBImageRec *TextureImage = NULL;
if ((TextureImage = LoadBMP(decalBmpFile)))
{
glGenTextures(1,&decalTexture);
glBindTexture(GL_TEXTURE_2D,decalTexture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,TextureImage->sizeX,
TextureImage->sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,
TextureImage->data);//指定纹理
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//指定过滤模式
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
else
status = 0;
if (TextureImage)
{
if (TextureImage->data)
free(TextureImage->data);
free(TextureImage);
}
return status;
}
//load cube map from 6 bmp files
int loadCubeMap()
{
int status = 1;
AUX_RGBImageRec *TextureImage[6] = {NULL,NULL,NULL,NULL,NULL,NULL};
for (loop = 0;loop < 6;++loop)
{
if (!(TextureImage[loop] = LoadBMP(bmpFile[loop])))
{
cout << "ERROR :load bmp file " << loop << " failed !" << endl;
status = 0;
}
}
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, TextureImage[0] ->sizeX, TextureImage[0] ->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0] ->data);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, TextureImage[1] ->sizeX, TextureImage[1] ->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[1] ->data);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, TextureImage[2] ->sizeX, TextureImage[2] ->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[2] ->data);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, TextureImage[3] ->sizeX, TextureImage[3] ->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[3] ->data);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, TextureImage[4] ->sizeX, TextureImage[4] ->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[4] ->data);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, TextureImage[5] ->sizeX, TextureImage[5] ->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[5] ->data);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//free memory
for (loop = 0;loop < 6;++loop)
{
if (TextureImage[loop])
{
if (TextureImage[loop] ->data)
{
free(TextureImage[loop] ->data);
}
free(TextureImage[loop]);
}
}
return status;
}
//draw th surroundings as a cube with each face of
//the cube environment map applied.
void drawSurroundings(const GLfloat *eyePosition)
{
const float surroundingsDistance = 8;
glLoadIdentity();
gluLookAt(eyePosition[0],eyePosition[1],eyePosition[2],
0,0,0,0,1,0);
glScalef(surroundingsDistance,
surroundingsDistance,
surroundingsDistance);
glEnable(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP,TO_ENVIRONMENT);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
glBegin(GL_QUADS);
for (loop = 0;loop < 4 * 6;++loop)
{
glTexCoord3fv(vertex[loop]);
glVertex3fv(vertex[loop]);
}
glEnd();
}
static void checkForCgError(const char *situation)
{
CGerror error;
const char *string = cgGetLastErrorString(&error);
if (error != CG_NO_ERROR) {
cout << "ERROR : " << myProgramName << situation << string << endl;
if (error == CG_COMPILER_ERROR) {
cout << cgGetLastListing(myCgContext) << endl;
}
exit(1);
}
}
//init Cg shaders
void initCg()
{
myCgContext = cgCreateContext();
myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
cgGLSetOptimalOptions(myCgVertexProfile);
checkForCgError("selecting vertex profile");
myCgVertexProgram = cgCreateProgramFromFile(
myCgContext,
CG_SOURCE,
myVertexProgramFileName,
myCgVertexProfile,
myVertexProgramName,
NULL);
checkForCgError("Creating vertex Cg program from file");
cgGLLoadProgram(myCgVertexProgram);
checkForCgError("loading vertex program");
myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
cgGLSetOptimalOptions(myCgFragmentProfile);
checkForCgError("selecting fragment profile");
myCgFragmentProgram = cgCreateProgramFromFile(
myCgContext,
CG_SOURCE,
myFragmentProgramFileName,
myCgFragmentProfile,
myFragmentProgramName,
NULL);
checkForCgError("Creating fragment Cg program from file");
cgGLLoadProgram(myCgFragmentProgram);
checkForCgError("loading fragment program");
}
//compute rotate transformation matrix
void makeRotateMatrix(float angle,
float ax,float ay,float az,
float m[16])
{
float radians, sine, cosine, ab, bc, ca, tx, ty, tz;
float axis[3];
float mag;
axis[0] = ax;
axis[1] = ay;
axis[2] = az;
mag = sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
if (mag) {
axis[0] /= mag;
axis[1] /= mag;
axis[2] /= mag;
}
radians = angle * myPi / 180.0;
sine = sin(radians);
cosine = cos(radians);
ab = axis[0] * axis[1] * (1 - cosine);
bc = axis[1] * axis[2] * (1 - cosine);
ca = axis[2] * axis[0] * (1 - cosine);
tx = axis[0] * axis[0];
ty = axis[1] * axis[1];
tz = axis[2] * axis[2];
m[0] = tx + cosine * (1 - tx);
m[1] = ab + axis[2] * sine;
m[2] = ca - axis[1] * sine;
m[3] = 0.0f;
m[4] = ab - axis[2] * sine;
m[5] = ty + cosine * (1 - ty);
m[6] = bc + axis[0] * sine;
m[7] = 0.0f;
m[8] = ca + axis[1] * sine;
m[9] = bc - axis[0] * sine;
m[10] = tz + cosine * (1 - tz);
m[11] = 0;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
}
//compute translation transformation matrix
static void makeTranslateMatrix(float x, float y, float z, float m[16])
{
m[0] = 1; m[1] = 0; m[2] = 0; m[3] = x;
m[4] = 0; m[5] = 1; m[6] = 0; m[7] = y;
m[8] = 0; m[9] = 0; m[10] = 1; m[11] = z;
m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
}
//multiply a floar4x4 matrix by another float4x4 matrix
static void multMatrix(float dst[16],const float src1[16],const float src2[16])
{
for (int i = 0;i < 4;++i)
{
for (int j = 0;j < 4;++j)
{
dst[i * 4 + j] = src1[i * 4 + 0] * src2[0 * 4 + j] +
src1[i * 4 + 1] * src2[1 * 4 + j] +
src1[i * 4 + 2] * src2[2 * 4 + j] +
src1[i * 4 + 3] * src2[3 * 4 + j];
}
}
}
void init()
{
glewInit();
glClearColor(0.0,0.0,0.0,1.0);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
if (!loadDecalTexture())
{
cout << "ERROR : load decal texture from bmp file failed !" << endl;
exit(1);
}
glBindTexture(GL_TEXTURE_CUBE_MAP,TO_ENVIRONMENT);
if (!loadCubeMap())
{
cout << "ERROR : load cube map from bmp file failed !" << endl;
exit(1);
}
initCg();
}
void display()
{
const GLfloat eyePosition[4] = {6 * sin(eyeAngle),
eyeHeight,
6 * cos(eyeAngle),
1};
float tranlateMatrix[16],rotateMatrix[16],modelMatrix[16];
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
cgGLEnableProfile(myCgVertexProfile);
checkForCgError("enabling vertex profile");
cgGLEnableProfile(myCgFragmentProfile);
checkForCgError("enabling fragment profile");
cgGLBindProgram(myCgVertexProgram);
checkForCgError("binding vertex program");
cgGLBindProgram(myCgFragmentProgram);
checkForCgError("binding fragment program");
glLoadIdentity();
glTranslatef(0.0,0.0,-5.0);
glRotatef(headSpain,0,1,0);
//set some uniform parameters in Cg shader
cgGLSetParameter3fv(
cgGetNamedParameter(myCgVertexProgram,"eyePositionW"),
eyePosition);
checkForCgError("setting eyePositionW parameter");
makeRotateMatrix(headSpain,0,1,0,rotateMatrix);
makeTranslateMatrix(0.0,0.0,-5.0,tranlateMatrix);
multMatrix(modelMatrix,tranlateMatrix,rotateMatrix);
//set the Cg matrix parameter : modelToWorld
cgSetMatrixParameterfr(
cgGetNamedParameter(myCgVertexProgram,"modelToWorld"),
modelMatrix);
checkForCgError("setting modelToWorld parameter");
cgGLSetParameter1f(
cgGetNamedParameter(myCgFragmentProgram,"reflectivity"),
reflectivity);
checkForCgError("setting reflectivity parameter");
cgGLSetTextureParameter(
cgGetNamedParameter(myCgFragmentProgram,"decalMap"),
decalTexture);
checkForCgError("setting decalTexture parameter");
cgGLSetTextureParameter(
cgGetNamedParameter(myCgFragmentProgram,"environmentMap"),
TO_ENVIRONMENT);
checkForCgError("setting environmentMap parameter");
drawMonkeyHead();
cgGLDisableProfile(myCgVertexProfile);
checkForCgError("disabling vertex profile");
cgGLDisableProfile(myCgFragmentProfile);
checkForCgError("disabling fragment profile");
drawSurroundings(eyePosition);
glutSwapBuffers();
}
static void idle()
{
headSpain += 0.5;
if (headSpain > 360)
{
headSpain -= 360;
}
glutPostRedisplay();
}
static void keyboard(unsigned char key,int x,int y)
{
switch(key)
{
case ' ':
animating = !animating;
if (animating)
{
glutIdleFunc(idle);
}
else
glutIdleFunc(NULL);
break;
case 'r':
reflectivity += 0.1;
if (reflectivity > 1.0)
{
reflectivity = 1.0;
}
cout << "reflectivity : " << reflectivity << endl;
glutPostRedisplay();
break;
case 'R':
reflectivity -= 0.1;
if (reflectivity < 0.0)
{
reflectivity = 0.0;
}
cout << "reflectivity : " << reflectivity << endl;
glutPostRedisplay();
break;
case 27:
cgDestroyProgram(myCgVertexProgram);
cgDestroyContext(myCgContext);
exit(0);
break;
}
}
void reshape(int w,int h)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0,1,1.0,20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc,char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(0,0);
glutInitWindowSize(600,600);
glutCreateWindow("CubeMapReflection");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
The first thing I see is that the lerp statement needs to have it's values reversed.
color.xyz = lerp(reflectedColor,decalColor,reflectivity);//change !!!!!!!!
should be
color.xyz = lerp(decalColor, reflectedColor, reflectivity);
because the lerp documentation says:
lerp(a, b, w) returns a when w = 0 and b when w = 1 and you want full decal when reflectivity = 0 and full reflected when reflectivity = 1.
I see that the effect you're trying to achieve is akin to GL_MODULATE. You will need to multiple the values together, not lerp between them. Try this, it should work and give you the effect you want.
color.xyz = (reflectedColor.xyz * reflectivity) * decalColor;
Related
Recently, I want to achieve interactive rotation operations as can be done in meshlab:
Basically, it achieves rotation of three degrees of freedom. I visualize these operations as following codes with the help of GLFW:
static void mouse_move_callback(GLFWwindow* window, double xpos, double ypos){
...
do{
//perform rotation operations only if keeping the right mouse key pressed
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_RELEASE) {
g_clr_right_mouse = true;
break;
}
/*clear mouse state once transferred from release state
to pressed state to prevent from a instant flicker*/
if(g_clr_right_mouse){
g_lastX = xpos;
g_lastY = ypos;
g_clr_right_mouse = false;
}
float xoffset = xpos - g_lastX; //let movement from down to top positive
float yoffset = g_lastY - ypos;
g_lastX = xpos;
g_lastY = ypos;
//do counterclockwise rotation around x-asis with movement in y direction
glm::mat4 r1 = glm::rotate(glm::mat4(), glm::radians(-yoffset * 0.5f), glm::vec3(1.0f,0.0f,0.0f));
//do counterclockwise rotation around y-asis with movement in x direction
glm::mat4 r2 = glm::rotate(glm::mat4(), glm::radians( xoffset * 0.5f), glm::vec3(0.0f,1.0f,0.0f));
glm::mat4 tmp = r2 * r1 * g_model;
for(int i=0; i<3; i++)
g_model[i] = tmp[i];
return ;
}while(false);
}
These codes are located here, and the whole project can be found here which can be downloaded and built. Finally, it performs as follows:
However, my implementation can only achieve rotation operations of 2 DOF, I add a keyboard callback to achieve rotation around the z axis:
void keyboard_callback(GLFWwindow* window, int key, int scancode, int action, int mod){
if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS){
glm::mat4 r3 = glm::rotate(glm::mat4(), glm::radians(3.0f), glm::vec3(0,0,1.0f));
glm::mat4 tmp = r3 * g_model;
for(int i=0; i<3; i++)
g_model[i] = tmp[i];
}else if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS){
glm::mat4 r3 = glm::rotate(glm::mat4(), glm::radians(-3.0f), glm::vec3(0,0,1.0f));
glm::mat4 tmp = r3 * g_model;
for(int i=0; i<3; i++)
g_model[i] = tmp[i];
}
}
So my question is how to decently achieve interactive rotation operations of 3 DOF only with mouse movement?
When dragging the mouse, the object must be rotated around an axis that is perpendicular to the direction of movement of the mouse. The pivot is the origin of the model.
Rotate the mouse movement vector by 90 ° in the XY plane of the view. Since this is a vector in view space, the vector must be transformed from view space into world space. The matrix that transforms a vector from view space to world space is the inverse matrix of the upper left 3x3 of the view matrix:
vec2 drag_start;
vec2 drag_end;
glm::mat3 to_world = glm::inverse(glm::mat3(view_matrix));
glm::vec2 drag_vec = glm::vec2(drag_end.x - drag_start.x, drag_start.y - drag_end.y);
glm::vec3 axis_vec = glm::normalize(to_world * glm::vec3(-drag_vec.y, drag_vec.x, 0));
Create a rotation matrix around the axis. The angle depends on the length of the vector (height is the height of the viewport in pixels):
GLfloat angle = glm::length(drag_vec) / height / 2 * M_PI;
drag_rotation = glm::rotate(glm::mat4(1.0f), angle, axis_vec);
Compute a rotation matrix while dragging the mouse. Concatenate the rotation matrix and the model matrix after the drag ends:
glm::mat4 view_matrix(1.0f);
glm::mat4 model_rotation(1.0f);
glm::mat4 drag_rotation(1.0f);
glm::vec2 drag_start(0.0f);
bool drag = false;
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button != GLFW_MOUSE_BUTTON_LEFT)
return;
if (action == GLFW_PRESS)
{
drag = true;
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
drag_start = glm::vec2(xpos, ypos);
}
else if (action == GLFW_RELEASE)
{
drag = false;
model_rotation = drag_rotation * model_rotation;
drag_rotation = glm::mat4(1.0f);
}
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
if (!drag)
return;
glm::mat3 to_world = glm::inverse(glm::mat3(view_matrix));
glm::vec2 drag_vec = glm::vec2(xpos - drag_start.x, drag_start.y - ypos);
glm::vec3 axis_vec = glm::normalize(to_world * glm::vec3(-drag_vec.y, drag_vec.x, 0));
GLfloat angle = glm::length(drag_vec) / height / 2 * M_PI;
drag_rotation = glm::rotate(glm::mat4(1.0f), angle, axis_vec);
}
The model matrix is the concatenation of drag_rotation and model_rotation:
glm::mat4 model = drag_rotation * model_rotation;
See also Orbit
Complete example:
#include <GL/glew.h>
#include <GL/gl.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <GLFW/glfw3.h>
#include <vector>
#include <string>
#include <stdexcept>
#include <iostream>
#define _USE_MATH_DEFINES
#include <cmath>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
std::string sh_vert = R"(
#version 460 core
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec3 a_uvw;
out vec3 v_uvw;
layout (location = 0) uniform mat4 u_projection;
layout (location = 1) uniform mat4 u_view;
layout (location = 2) uniform mat4 u_model;
void main()
{
v_uvw = a_uvw;
gl_Position = u_projection * u_view * u_model * a_position;
}
)";
std::string sh_frag = R"(
#version 460 core
out vec4 frag_color;
in vec3 v_uvw;
vec3 HUEtoRGB(in float H)
{
float R = abs(H * 6.0 - 3.0) - 1.0;
float G = 2.0 - abs(H * 6.0 - 2.0);
float B = 2.0 - abs(H * 6.0 - 4.0);
return clamp(vec3(R, G, B), 0.0, 1.0);
}
void main()
{
frag_color = vec4(HUEtoRGB(v_uvw.z), 1.0);
}
)";
class ShaderProgram
{
public:
GLuint programObject;
static ShaderProgram newProgram(const std::string& vsh, const std::string& fsh);
private:
GLuint compileShader(const std::string& sourceCode, GLenum shaderType);
void linkProgram(std::vector<GLuint> shObjs);
void compileStatus(GLuint shader);
void linkStatus();
};
class VertexArrayObject
{
public:
GLuint vaoObject = 0;
GLsizei noOfVertices = 0;
GLsizei noOfIndices = 0;
static VertexArrayObject newCube();
static VertexArrayObject newCircles();
static VertexArrayObject newVAO(const std::vector<GLfloat>& varray, const std::vector<GLuint>& iarray);
};
int width = 800, height = 600;
glm::mat4 view_matrix(1.0f);
glm::mat4 model_rotation(1.0f);
glm::mat4 drag_rotation(1.0f);
glm::vec2 drag_start(0.0f);
bool drag = false;
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button != GLFW_MOUSE_BUTTON_LEFT)
return;
if (action == GLFW_PRESS)
{
drag = true;
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
drag_start = glm::vec2(xpos, ypos);
}
else if (action == GLFW_RELEASE)
{
drag = false;
model_rotation = drag_rotation * model_rotation;
drag_rotation = glm::mat4(1.0f);
}
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
if (!drag)
return;
glm::mat3 to_world = glm::inverse(glm::mat3(view_matrix));
glm::vec2 drag_vec = glm::vec2(xpos - drag_start.x, drag_start.y - ypos);
glm::vec3 axis_vec = glm::normalize(to_world * glm::vec3(-drag_vec.y, drag_vec.x, 0));
GLfloat angle = glm::length(drag_vec) / height / 2 * M_PI;
drag_rotation = glm::rotate(glm::mat4(1.0f), angle, axis_vec);
}
int main(void)
{
if (glfwInit() == GLFW_FALSE)
throw std::runtime_error( "error initializing glfw" );
glfwWindowHint(GLFW_SAMPLES, 8);
GLFWwindow * window = glfwCreateWindow(width, height, "OGL window", nullptr, nullptr);
if (window == nullptr)
{
glfwTerminate();
throw std::runtime_error( "error initializing window" );
}
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwMakeContextCurrent(window);
if ( glewInit() != GLEW_OK )
throw std::runtime_error( "error initializing glew" );
auto progam = ShaderProgram::newProgram(sh_vert, sh_frag);
auto cube = VertexArrayObject::newCube();
auto circles = VertexArrayObject::newCircles();
glUseProgram(progam.programObject);
glEnable( GL_DEPTH_TEST );
glClearColor(0.1f, 0.3f, 0.2f, 0.0f);
view_matrix = glm::lookAt(glm::vec3(0.0f, 0.0f, 7.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glUniformMatrix4fv(1, 1, GL_FALSE, glm::value_ptr(view_matrix));
while (!glfwWindowShouldClose(window))
{
glfwGetFramebufferSize(window, &width, &height);
float ascpect = (float)width / (float)height;
glm::mat4 project = glm::perspective(glm::radians(60.0f), ascpect, 0.1f, 20.0f);
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(project));
glm::mat4 model = drag_rotation * model_rotation;
glViewport(0, 0, width, height);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glUniformMatrix4fv(2, 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(cube.vaoObject);
glDrawElements(GL_TRIANGLES, cube.noOfIndices, GL_UNSIGNED_INT, nullptr);
glUniformMatrix4fv(2, 1, GL_FALSE, glm::value_ptr(glm::scale(model, glm::vec3(2.5f))));
glBindVertexArray(circles.vaoObject);
glDrawElements(GL_LINES, circles.noOfIndices, GL_UNSIGNED_INT, nullptr);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
ShaderProgram ShaderProgram::newProgram(const std::string& vsh, const std::string& fsh)
{
ShaderProgram program;
auto shObjs = std::vector<GLuint>
{
program.compileShader(vsh, GL_VERTEX_SHADER),
program.compileShader(fsh, GL_FRAGMENT_SHADER),
};
for (auto shObj : shObjs)
program.compileStatus(shObj);
program.linkProgram(shObjs);
for (auto shObj : shObjs)
glDeleteShader(shObj);
return program;
}
GLuint ShaderProgram::compileShader(const std::string& sourceCode, GLenum shaderType)
{
auto shaderObj = glCreateShader(shaderType);
const char* srcCodePtr = sourceCode.c_str();
glShaderSource(shaderObj, 1, &srcCodePtr, nullptr);
glCompileShader(shaderObj);
return shaderObj;
}
void ShaderProgram::linkProgram(std::vector<GLuint> shObjs)
{
programObject = glCreateProgram();
for (auto shObj : shObjs)
glAttachShader(programObject, shObj);
glLinkProgram(programObject);
linkStatus();
}
void ShaderProgram::compileStatus(GLuint shader)
{
GLint status = GL_TRUE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint logLen;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLen);
std::vector< char >log(logLen);
GLsizei written;
glGetShaderInfoLog(shader, logLen, &written, log.data());
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
}
void ShaderProgram::linkStatus()
{
GLint status = GL_TRUE;
glGetProgramiv(programObject, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint logLen;
glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &logLen);
std::vector< char >log(logLen);
GLsizei written;
glGetProgramInfoLog(programObject, logLen, &written, log.data());
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
}
VertexArrayObject VertexArrayObject::newCube()
{
static const std::vector<GLfloat> vertices{ -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1 };
static const std::vector<GLfloat> uv{ 0, 0, 1, 0, 1, 1, 0, 1 };
static const std::vector<size_t> faces{ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 };
std::vector<GLfloat> varray;
std::vector<GLuint> iarray;
for (auto si = 0; si < faces.size() / 4; si++)
{
for (auto qi = 0; qi < 4; qi++)
{
varray.insert(varray.end(), vertices.begin() + faces[si * 4 + qi] * 3, vertices.begin() + faces[si * 4 + qi] * 3 + 3);
std::vector<GLfloat> uvw{ 0, 0, (GLfloat)si * 4.0f / (GLfloat)faces.size() };
varray.insert(varray.end(), uvw.begin(), uvw.end());
}
std::vector<GLuint> indices{ 4u * si, 4u * si + 1, 4u * si + 2, 4u * si, 4u * si + 2, 4u * si + 3 };
iarray.insert(iarray.end(), indices.begin(), indices.end());
}
return newVAO(varray, iarray);
}
VertexArrayObject VertexArrayObject::newCircles()
{
const GLuint noC = 360;
std::vector<GLfloat> varray;
std::vector<GLuint> iarray;
for (int i = 0; i <= noC; i++)
{
GLfloat angle = static_cast<GLfloat>(i * 2 * M_PI / noC);
GLfloat c = cos(angle), s = sin(angle);
std::vector<GLfloat> va{ 0, c, s, 0, 0, 0, s, 0, c, 0, 0, 1.0f / 3.0f, c, s, 0, 0, 0, 2.0f / 3.0f };
varray.insert(varray.end(), va.begin(), va.end());
}
for (GLuint ci = 0; ci < 3; ci++)
{
for (GLuint i = 0; i <= noC; i++)
{
std::vector<GLuint> ia{ i * 3 + ci, ((i + 1) % noC) * 3 + ci };
iarray.insert(iarray.end(), ia.begin(), ia.end());
}
}
return newVAO(varray, iarray);
}
VertexArrayObject VertexArrayObject::newVAO(const std::vector<GLfloat>& varray, const std::vector<GLuint>& iarray)
{
VertexArrayObject vao;
vao.noOfIndices = static_cast<GLsizei>(iarray.size());
vao.noOfVertices = static_cast<GLsizei>(varray.size() / 6);
GLuint bufferObjects[2];
glGenBuffers(2, bufferObjects);;
glGenVertexArrays(1, &vao.vaoObject);
glBindVertexArray(vao.vaoObject);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, bufferObjects[0]);
glBufferData(GL_ARRAY_BUFFER, varray.size() * sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*varray.data()), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*varray.data()), (void*)(3 * sizeof(*varray.data())));
if (vao.noOfIndices > 0)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iarray.size() * sizeof(*iarray.data()), iarray.data(), GL_STATIC_DRAW);
}
glBindVertexArray(0);
glDeleteBuffers(2, bufferObjects);
return vao;
}
You kinda need to draw the ball to make it intuitive.
On mouse down, you put an anchor on the ball directly under the mouse pointer. If the click is outside the ball, then you use the closest point on the ball.
As the mouse moves, you rotate the ball so that the anchor point follows the shortest path so that it remains directly under the mouse pointer. If the mouse pointer is off the ball, then the closest point on the ball is used.
Maybe this will help.
I am having a problem with Opengl and Opengl ES. I want to make squares with png image textures and render text in front of these squares.
I can make squares with textures from png images, and I can render text from ttf like in this example, but it works only if not executing at the same time. I will try to explain better (The example code is too dirty because the code I'm getting from one software is more structured and bigger, and this code is only for an example):
I have one GLProgram with one Vertex Shader and one Fragment Shader for texture squares. I have another GLProgram with another Vertex Shader and another Fragment Shader to make Renderer text.
If I load all the GL programs and only draw each frame only the texture square, I can see it perfectly.
If I draw the renderer text (using his GLprogram) before or after the texture square (doing his glUseProgram before drawing the square) I can only see the texture text on screen and the background color that's created with (glClearColor(x.xf, x.xf, x.xf, x.xf)) and I can't see the texture square.
Does anyone know where the error is in the code?
Now I will post the shaders and the example source code:
back.v.glsl:
attribute vec4 g_vPosition;
attribute vec3 g_vColor;
attribute vec2 g_vTexCoord;
varying vec3 g_vVSColor;
varying vec2 g_vVSTexCoord;
void main()
{
gl_Position = g_vPosition;
g_vVSColor = g_vColor;
g_vVSTexCoord = g_vTexCoord;
}
back.f.glsl:
uniform sampler2D s_texture;
varying vec3 g_vVSColor;
varying vec2 g_vVSTexCoord;
void main()
{
gl_FragColor = texture2D(s_texture,g_vVSTexCoord);
}
text.v.glsl:
attribute vec4 coord;
varying vec2 texpos;
void main(void) {
gl_Position = vec4(coord.xy, 0, 1);
texpos = coord.zw;
}
text.f.glsl:
varying vec2 texpos;
uniform sampler2D tex;
uniform vec4 color;
void main(void) {
//gl_FragColor = vec4(1, 1, 1, texture2D(tex, texpos).a) * color;
gl_FragColor = vec4(color.rgb, texture2D(tex, texpos).a);
}
Source Code:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <math.h>
#include <signal.h>
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <unistd.h>
#include <sys/time.h>
#include <png.h>
EGLDisplay egldisplay;
EGLConfig eglconfig;
EGLSurface eglsurface;
EGLContext eglcontext;
EGLNativeWindowType eglNativeWindow;
EGLNativeDisplayType eglNativeDisplayType;
#include <ft2build.h>
#include FT_FREETYPE_H
GLuint program;
GLint attribute_coord;
GLint uniform_tex;
GLint uniform_color;
GLuint programBack;
GLint coordBack = 0;
GLint texBack = 2;
GLint colorBack = 1;
struct point {
GLfloat x;
GLfloat y;
GLfloat s;
GLfloat t;
};
GLuint vbo;
FT_Library ft;
FT_Face face;
// Maximum texture width
#define MAXWIDTH 800
const char *fontfilename;
float VertexColors[] = {
/* Red */
1.0f, 0.0f, 0.0f, 1.0f,
/* Red */
1.0f, 0.0f, 0.0f, 1.0f,
/* Green */
0.0f, 1.0f, 0.0f, 1.0f,
/* Green */
0.0f, 1.0f, 0.0f, 1.0f,
};
float VertexTexCoords[] = {
/* Front Face */
0.0f,0.0f,
1.0f,0.0f,
0.0f,1.0f,
1.0f,1.0f,
};
float fBackgroundPosition[12] = {
/* Bottom Left Of The Quad (Front) */
-1.0f,-1.0f,1.0f,
/* Bottom Right Of The Quad (Front) */
1.0f,-1.0f,1.0f,
/* Top Left Of The Quad (Front) */
-1.0f,1.0f,1.0f,
/* Top Right Of The Quad (Front) */
1.0f,1.0f,1.0f,
};
GLuint glTextures[4];
/**
* The atlas struct holds a texture that contains the visible US-ASCII characters
* of a certain font rendered with a certain character height.
* It also contains an array that contains all the information necessary to
* generate the appropriate vertex and texture coordinates for each character.
*
* After the constructor is run, you don't need to use any FreeType functions anymore.
*/
struct atlas {
GLuint tex; // texture object
unsigned int w; // width of texture in pixels
unsigned int h; // height of texture in pixels
struct {
float ax; // advance.x
float ay; // advance.y
float bw; // bitmap.width;
float bh; // bitmap.height;
float bl; // bitmap_left;
float bt; // bitmap_top;
float tx; // x offset of glyph in texture coordinates
float ty; // y offset of glyph in texture coordinates
} c[256]; // character information
atlas(FT_Face face, int height) {
FT_Set_Pixel_Sizes(face, 0, height);
FT_GlyphSlot g = face->glyph;
unsigned int roww = 0;
unsigned int rowh = 0;
w = 0;
h = 0;
memset(c, 0, sizeof c);
/* Find minimum size for a texture holding all visible ASCII characters */
//for (int i = 32; i < 128; i++) {
for (int i = 32; i < 254; i++) {
if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {
fprintf(stderr, "Loading character %c failed!\n", i);
continue;
}
if (roww + g->bitmap.width + 1 >= MAXWIDTH) {
w = std::max(w, roww);
h += rowh;
roww = 0;
rowh = 0;
}
roww += g->bitmap.width + 1;
rowh = std::max(rowh, g->bitmap.rows);
}
w = std::max(w, roww);
h += rowh;
/* Create a texture that will be used to hold all ASCII glyphs */
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glUniform1i(uniform_tex, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
/* We require 1 byte alignment when uploading texture data */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/* Clamping to edges is important to prevent artifacts when scaling */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
/* Linear filtering usually looks best for text */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* Paste all glyph bitmaps into the texture, remembering the offset */
int ox = 0;
int oy = 0;
rowh = 0;
//for (int i = 32; i < 128; i++) {
for (int i = 32; i < 254; i++) {
if (FT_Load_Char(face, i, FT_LOAD_RENDER)) {
fprintf(stderr, "Loading character %c failed!\n", i);
continue;
}
if (ox + g->bitmap.width + 1 >= MAXWIDTH) {
oy += rowh;
rowh = 0;
ox = 0;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
c[i].ax = g->advance.x >> 6;
c[i].ay = g->advance.y >> 6;
c[i].bw = g->bitmap.width;
c[i].bh = g->bitmap.rows;
c[i].bl = g->bitmap_left;
c[i].bt = g->bitmap_top;
c[i].tx = ox / (float)w;
c[i].ty = oy / (float)h;
rowh = std::max(rowh, g->bitmap.rows);
ox += g->bitmap.width + 1;
}
fprintf(stderr, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024);
}
~atlas() {
glDeleteTextures(1, &tex);
}
};
atlas *a48;
atlas *a24;
atlas *a12;
//#define GL_ES_VERSION_2_0
/**
* Store all the file's contents in memory, useful to pass shaders
* source code to OpenGL
*/
char* file_read(const char* filename)
{
FILE* in = fopen(filename, "rb");
if (in == NULL) return NULL;
int res_size = BUFSIZ;
char* res = (char*)malloc(res_size);
int nb_read_total = 0;
while (!feof(in) && !ferror(in)) {
if (nb_read_total + BUFSIZ > res_size) {
if (res_size > 10 * 1024 * 1024) break;
res_size = res_size * 2;
res = (char*)realloc(res, res_size);
}
char* p_res = res + nb_read_total;
nb_read_total += fread(p_res, 1, BUFSIZ, in);
}
fclose(in);
res = (char*)realloc(res, nb_read_total + 1);
res[nb_read_total] = '\0';
return res;
}
/**
* Compile the shader from file 'filename', with error handling
*/
GLuint create_shader(const char* filename, GLenum type)
{
const GLchar* source = file_read(filename);
if (source == NULL) {
fprintf(stderr, "Error opening %s: ", filename); perror("");
return 0;
}
else {
printf("Load shader correctly %s\n", filename);
}
GLuint res = glCreateShader(type);
const GLchar* sources[] = {
// Define GLSL version
#ifdef GL_ES_VERSION_2_0
"#version 100\n" // OpenGL ES 2.0
#else
"#version 120\n" // OpenGL 2.1
#endif
,
// GLES2 precision specifiers
#ifdef GL_ES_VERSION_2_0
// Define default float precision for fragment shaders:
(type == GL_FRAGMENT_SHADER) ?
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
"precision highp float; \n"
"#else \n"
"precision mediump float; \n"
"#endif \n"
: ""
// Note: OpenGL ES automatically defines this:
// #define GL_ES
#else
// Ignore GLES 2 precision specifiers:
"#define lowp \n"
"#define mediump\n"
"#define highp \n"
#endif
,
source };
glShaderSource(res, 3, sources, NULL);
free((void*)source);
glCompileShader(res);
GLint compile_ok = GL_FALSE;
glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
if (compile_ok == GL_FALSE) {
fprintf(stderr, "%s:", filename);
//print_log(res);
glDeleteShader(res);
return 0;
}
return res;
}
GLuint create_program(const char *vertexfile, const char *fragmentfile) {
printf("Creating program\n");
GLuint program = glCreateProgram();
GLuint shader;
printf("Loading program\n");
if (vertexfile) {
shader = create_shader(vertexfile, GL_VERTEX_SHADER);
if (!shader)
return 0;
glAttachShader(program, shader);
}
if (fragmentfile) {
shader = create_shader(fragmentfile, GL_FRAGMENT_SHADER);
if (!shader)
return 0;
glAttachShader(program, shader);
}
glLinkProgram(program);
GLint link_ok = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
if (!link_ok) {
fprintf(stderr, "glLinkProgram:");
//print_log(program);
glDeleteProgram(program);
return 0;
}
return program;
}
GLint get_attrib(GLuint program, const char *name) {
GLint attribute = glGetAttribLocation(program, name);
if (attribute == -1)
fprintf(stderr, "Could not bind attribute %s\n", name);
return attribute;
}
GLint get_uniform(GLuint program, const char *name) {
GLint uniform = glGetUniformLocation(program, name);
if (uniform == -1)
fprintf(stderr, "Could not bind uniform %s\n", name);
return uniform;
}
bool bLoadPngImage(char *name, int &outWidth, int &outHeight, bool &outHasAlpha, GLubyte **outData) {
png_structp png_ptr;
png_infop info_ptr;
unsigned int sig_read = 0;
int color_type, interlace_type;
FILE *fp;
if ((fp = fopen(name, "rb")) == NULL)
return false;
/* Create and initialize the png_struct
* with the desired error handler
* functions. If you want to use the
* default stderr and longjump method,
* you can supply NULL for the last
* three parameters. We also supply the
* the compiler header file version, so
* that we know if the application
* was compiled with a compatible version
* of the library. REQUIRED
*/
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(fp);
return false;
}
/* Allocate/initialize the memory
* for image information. REQUIRED. */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(fp);
png_destroy_read_struct(&png_ptr, NULL, NULL);
return false;
}
/* Set error handling if you are
* using the setjmp/longjmp method
* (this is the normal method of
* doing things with libpng).
* REQUIRED unless you set up
* your own error handlers in
* the png_create_read_struct()
* earlier.
*/
if (setjmp(png_jmpbuf(png_ptr))) {
/* Free all of the memory associated
* with the png_ptr and info_ptr */
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(fp);
/* If we get here, we had a
* problem reading the file */
return false;
}
/* Set up the output control if
* you are using standard C streams */
png_init_io(png_ptr, fp);
/* If we have already
* read some of the signature */
png_set_sig_bytes(png_ptr, sig_read);
/*
* If you have enough memory to read
* in the entire image at once, and
* you need to specify only
* transforms that can be controlled
* with one of the PNG_TRANSFORM_*
* bits (this presently excludes
* dithering, filling, setting
* background, and doing gamma
* adjustment), then you can read the
* entire image (including pixels)
* into the info structure with this
* call
*
* PNG_TRANSFORM_STRIP_16 |
* PNG_TRANSFORM_PACKING forces 8 bit
* PNG_TRANSFORM_EXPAND forces to
* expand a palette into RGB
*/
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
png_uint_32 width, height;
int bit_depth;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
outWidth = width;
outHeight = height;
unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr);
*outData = (unsigned char*)malloc(row_bytes * outHeight);
png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);
for (int i = 0; i < outHeight; i++) {
// note that png is ordered top to
// bottom, but OpenGL expect it bottom to top
// so the order or swapped
memcpy(*outData + (row_bytes * (outHeight - 1 - i)), row_pointers[i], row_bytes);
}
/* Clean up after the read,
* and free any memory allocated */
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
/* Close the file */
fclose(fp);
/* That's it */
return true;
}
void vLoadPngToTexture(std::string sFilename, int iTexture) {
GLubyte *glTextureImage;
int iWidth, iHeight;
bool bHasAlpha;
//char filename[] = "/home/root/res/drawable/disclaimerantamina.png";
//bool success = bLoadPngImage((char *)sFilename.c_str(), iWidth, iHeight, bHasAlpha, &glTextureImage);
//if (!success) {
if (!bLoadPngImage((char *)sFilename.c_str(), iWidth, iHeight, bHasAlpha, &glTextureImage)) {
std::cout << "Unable to load png file" << std::endl;
exit(0);
}
std::cout << "Image loaded " << sFilename << " " << iWidth << " " << iHeight << " alpha " << bHasAlpha << std::endl;
glBindTexture(GL_TEXTURE_2D, glTextures[iTexture]);
/* Generate The Texture */
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iWidth,
iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
glTextureImage);
/* Linear Filtering */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
std::cout << "Texture loaded " << iTexture << " - " << sFilename << std::endl;
}
int init_resources() {
/* Initialize the FreeType2 library */
if (FT_Init_FreeType(&ft)) {
fprintf(stderr, "Could not init freetype library\n");
return 0;
}
/* Load a font */
if (FT_New_Face(ft, fontfilename, 0, &face)) {
fprintf(stderr, "Could not open font %s\n", fontfilename);
return 0;
}
printf("Load Font Correctly\n");
program = create_program("text.v.glsl", "text.f.glsl");
if (program == 0)
return 0;
printf("Create program Correctly\n");
attribute_coord = get_attrib(program, "coord");
uniform_tex = get_uniform(program, "tex");
uniform_color = get_uniform(program, "color");
if (attribute_coord == -1 || uniform_tex == -1 || uniform_color == -1)
return 0;
printf("Create attributes Correctly\n");
programBack = create_program("back.v.glsl", "back.f.glsl");
printf("Create program Background Correctly\n");
coordBack = get_attrib(programBack, "g_vPosition");
//colorBack = get_attrib(programBack, "g_vColor");
texBack = get_attrib(programBack, "g_vTexCoord");
if (coordBack == -1 || colorBack == -1 || texBack == -1)
return 0;
// Create the vertex buffer object
glGenBuffers(1, &vbo);
///* Create texture atlasses for several font sizes */
a48 = new atlas(face, 48);
a24 = new atlas(face, 24);
a12 = new atlas(face, 12);
return 1;
}
/**
* Render text using the currently loaded font and currently set font size.
* Rendering starts at coordinates (x, y), z is always 0.
* The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy).
*/
void render_text(const char *text, atlas * a, float x, float y, float sx, float sy) {
const uint8_t *p;
/* Use the texture containing the atlas */
glBindTexture(GL_TEXTURE_2D, a->tex);
glUniform1i(uniform_tex, 0);
/* Set up the VBO for our vertex data */
glEnableVertexAttribArray(attribute_coord);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(attribute_coord, 4, GL_FLOAT, GL_FALSE, 0, 0);
point coords[6 * strlen(text)];
int c = 0;
/* Loop through all characters */
for (p = (const uint8_t *)text; *p; p++) {
/* Calculate the vertex and texture coordinates */
float x2 = x + a->c[*p].bl * sx;
float y2 = -y - a->c[*p].bt * sy;
float w = a->c[*p].bw * sx;
float h = a->c[*p].bh * sy;
/* Advance the cursor to the start of the next character */
x += a->c[*p].ax * sx;
y += a->c[*p].ay * sy;
/* Skip glyphs that have no pixels */
if (!w || !h)
continue;
coords[c++] = (point) {
x2, -y2, a->c[*p].tx, a->c[*p].ty
};
coords[c++] = (point) {
x2 + w, -y2, a->c[*p].tx + a->c[*p].bw / a->w, a->c[*p].ty
};
coords[c++] = (point) {
x2, -y2 - h, a->c[*p].tx, a->c[*p].ty + a->c[*p].bh / a->h
};
coords[c++] = (point) {
x2 + w, -y2, a->c[*p].tx + a->c[*p].bw / a->w, a->c[*p].ty
};
coords[c++] = (point) {
x2, -y2 - h, a->c[*p].tx, a->c[*p].ty + a->c[*p].bh / a->h
};
coords[c++] = (point) {
x2 + w, -y2 - h, a->c[*p].tx + a->c[*p].bw / a->w, a->c[*p].ty + a->c[*p].bh / a->h
};
}
/* Draw all the character on the screen in one go */
glBufferData(GL_ARRAY_BUFFER, sizeof coords, coords, GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, c);
glDisableVertexAttribArray(attribute_coord);
}
void display() {
//float sx = 2.0 / glutGet(GLUT_WINDOW_WIDTH);
//float sy = 2.0 / glutGet(GLUT_WINDOW_HEIGHT);
float sx = 2.0 / 800;
float sy = 2.0 / 480;
glViewport(0, 0, 800, 480);
glUseProgram(programBack);
/* White background */
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* Enable blending, necessary for our alpha texture */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glVertexAttribPointer(coordBack, 3, GL_FLOAT, 0, 0, fBackgroundPosition);
glEnableVertexAttribArray(coordBack);
//glVertexAttribPointer(colorBack, 4, GL_FLOAT, 0, 0, VertexColors);
//glEnableVertexAttribArray(colorBack);
glVertexAttribPointer(texBack, 2, GL_FLOAT, 0, 0, VertexTexCoords);
glEnableVertexAttribArray(texBack);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glTextures[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Cleanup
glDisableVertexAttribArray(coordBack);
glDisableVertexAttribArray(colorBack);
glDisableVertexAttribArray(texBack);
glUseProgram(program);
GLfloat black[4] = { 0, 0, 0, 1 };
GLfloat red[4] = { 1, 0, 0, 1 };
GLfloat transparent_green[4] = { 0, 1, 0, 0.5 };
/* Set color to black */
//glUniform4fv(uniform_color, 1, black);
/* Effects of alignment */
render_text("The ñ Ñ Quick Brown Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 50 * sy, sx, sy);
//render_text("The , . - + { } & % Misaligned Fox Jumps Over The Lazy Dog", a48, -1 + 8.5 * sx, 1 - 100.5 * sy, sx, sy);
///* Scaling the texture versus changing the font size */
//render_text("The ç ó Small Texture Scaled Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 175 * sy, sx * 0.5, sy * 0.5);
//render_text("The Small Font Sized Fox Jumps Over The Lazy Dog", a24, -1 + 8 * sx, 1 - 200 * sy, sx, sy);
//render_text("The Tiny Texture Scaled Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 235 * sy, sx * 0.25, sy * 0.25);
//render_text("The Tiny Font Sized Fox Jumps Over The Lazy Dog", a12, -1 + 8 * sx, 1 - 250 * sy, sx, sy);
///* Colors and transparency */
//render_text("The Solid Black Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 430 * sy, sx, sy);
//glUniform4fv(uniform_color, 1, red);
//render_text("The Solid Red Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 330 * sy, sx, sy);
//render_text("The Solid Red Fox Jumps Over The Lazy Dog", a48, -1 + 28 * sx, 1 - 450 * sy, sx, sy);
//glUniform4fv(uniform_color, 1, transparent_green);
//render_text("The Transparent Green Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 380 * sy, sx, sy);
//render_text("The Transparent Green Fox Jumps Over The Lazy Dog", a48, -1 + 18 * sx, 1 - 440 * sy, sx, sy);
eglSwapBuffers(egldisplay, eglsurface);
}
void free_resources() {
glDeleteProgram(program);
}
int init(void)
{
Display *x_display;
Window win;
x_display = XOpenDisplay(":0"); // open the standard display (the primary screen)
if (x_display == NULL) {
std::cout << "cannot connect to X server" << std::endl;
return 1;
}
Window root = DefaultRootWindow(x_display); // get the root window (usually the whole screen)
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
win = XCreateWindow( // create a window with the provided parameters
x_display, root,
0, 0, 800, 480, 0,
CopyFromParent, InputOutput,
CopyFromParent, CWEventMask,
&swa);
XMapWindow(x_display, win); // make the window visible on the screen
XStoreName(x_display, win, "GL test"); // give the window a name
static const EGLint s_configAttribs[] =
{
EGL_RENDERABLE_TYPE, 4,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_SAMPLES, 0,
EGL_SAMPLE_BUFFERS, 1,
EGL_SAMPLES, 4, // This is for 4x MSAA.
EGL_NONE
};
EGLint numconfigs;
egldisplay = eglGetDisplay((EGLNativeDisplayType)x_display);
eglInitialize(egldisplay, NULL, NULL);
assert(eglGetError() == EGL_SUCCESS);
eglBindAPI(EGL_OPENGL_ES_API);
eglChooseConfig(egldisplay, s_configAttribs, &eglconfig, 1, &numconfigs);
assert(eglGetError() == EGL_SUCCESS);
assert(numconfigs == 1);
eglsurface = eglCreateWindowSurface(egldisplay, eglconfig, win, NULL);
assert(eglGetError() == EGL_SUCCESS);
EGLint ContextAttribList[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
eglcontext = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT, ContextAttribList);
assert(eglGetError() == EGL_SUCCESS);
eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext);
assert(eglGetError() == EGL_SUCCESS);
printf("VENDOR = %s\n", glGetString(GL_VENDOR));
printf("RENDERER = %s\n", glGetString(GL_RENDERER));
printf("VERSION = %s\n", glGetString(GL_VERSION));
}
int main(int argc, char *argv[]) {
if (argc > 1)
fontfilename = argv[1];
else
fontfilename = "FreeSans.ttf";
init();
init_resources();
std::string sBackground = "back2.png";
vLoadPngToTexture(sBackground, 0);
//// this is needed for time measuring --> frames per second
struct timezone tz;
timeval t1, t2;
gettimeofday(&t1, &tz);
int num_frames = 0;
while (1) {
display();
if (++num_frames % 30 == 0) {
//if (++num_frames % 100 == 0) {
gettimeofday(&t2, &tz);
float dt = t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) * 1e-6;
std::cout << "fps: " << num_frames / dt << std::endl;
num_frames = 0;
t1 = t2;
}
usleep(32000);
}
return 0;
}
If I comment the line render_text("The ñ Ñ Quick Brown Fox Jumps Over The Lazy Dog", a48, -1 + 8 * sx, 1 - 50 * sy, sx, sy);, I can see the textured square perfectly.
glUniform operates on the program object that was made part of current state by calling glUseProgram.
Because of that the call of glUniform1i in the function atlas is useless, because at this point there is no current program at all. You can delete this call, because you set the uniform later.
The texture glTextures[iTexture], which you bind in the function vLoadPngToTexture is not generated.
Change your code somehow like this:
glGenTextures(1, glTextures+iTexture);
glBindTexture(GL_TEXTURE_2D, glTextures[iTexture]);
You do not use an array buffer when you draw the background, but you use an array buffer when you draw the text. In the function render_text you bind glBindBuffer(GL_ARRAY_BUFFER, vbo); but you never release it. This causes that the array buffer vbois still bound when the background should be drawn in the next cycle. Becauseof that, glVertexAttribPointer, in the function display, does not do what you expect it to do.
You can fix this by adding
glBindBuffer(GL_ARRAY_BUFFER, 0)
to the end of the function render_text.
I got a code from my teacher that currently shows a 3D globe and a 2D particle system. The camera moves around in circles. The particle system is supposed to face the camera.
According to my lecture notes, I have to multiply the billboard with the inverse of the camera's view matrix. I would love to try that but I have trouble using the variable for the view matrix.
#include "pch.h"
#include <Kore/Application.h>
#include <Kore/IO/FileReader.h>
#include <Kore/Math/Core.h>
#include <Kore/Math/Random.h>
#include <Kore/System.h>
#include <Kore/Input/Keyboard.h>
#include <Kore/Input/Mouse.h>
#include <Kore/Audio/Mixer.h>
#include <Kore/Graphics/Image.h>
#include <Kore/Graphics/Graphics.h>
#include <Kore/Log.h>
#include "ObjLoader.h"
#include "Collision.h"
#include "PhysicsWorld.h"
#include "PhysicsObject.h"
using namespace Kore;
// A simple particle implementation
class Particle {
public:
VertexBuffer* vb;
IndexBuffer* ib;
mat4 M;
// The current position
vec3 position;
// The current velocity
vec3 velocity;
// The remaining time to live
float timeToLive;
// The total time time to live
float totalTimeToLive;
// Is the particle dead (= ready to be re-spawned?)
bool dead;
void init(const VertexStructure& structure) {
vb = new VertexBuffer(4, structure,0);
float* vertices = vb->lock();
SetVertex(vertices, 0, -1, -1, 0, 0, 0);
SetVertex(vertices, 1, -1, 1, 0, 0, 1);
SetVertex(vertices, 2, 1, 1, 0, 1, 1);
SetVertex(vertices, 3, 1, -1, 0, 1, 0);
vb->unlock();
// Set index buffer
ib = new IndexBuffer(6);
int* indices = ib->lock();
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 0;
indices[4] = 2;
indices[5] = 3;
ib->unlock();
dead = true;
}
void Emit(vec3 pos, vec3 velocity, float timeToLive) {
position = pos;
this->velocity = velocity;
dead = false;
this->timeToLive = timeToLive;
totalTimeToLive = timeToLive;
}
Particle() {
}
void SetVertex(float* vertices, int index, float x, float y, float z, float u, float v) {
vertices[index* 8 + 0] = x;
vertices[index*8 + 1] = y;
vertices[index*8 + 2] = z;
vertices[index*8 + 3] = u;
vertices[index*8 + 4] = v;
vertices[index*8 + 5] = 0.0f;
vertices[index*8 + 6] = 0.0f;
vertices[index*8 + 7] = -1.0f;
}
void render(TextureUnit tex, Texture* image) {
Graphics::setTexture(tex, image);
Graphics::setVertexBuffer(*vb);
Graphics::setIndexBuffer(*ib);
Graphics::drawIndexedVertices();
}
void Integrate(float deltaTime) {
timeToLive -= deltaTime;
if (timeToLive < 0.0f) {
dead = true;
}
// Note: We are using no forces or gravity at the moment.
position += velocity * deltaTime;
// Build the matrix
M = mat4::Translation(position.x(), position.y(), position.z()) * mat4::Scale(0.2f, 0.2f, 0.2f);
}
};
class ParticleSystem {
public:
// The center of the particle system
vec3 position;
// The minimum coordinates of the emitter box
vec3 emitMin;
// The maximal coordinates of the emitter box
vec3 emitMax;
// The list of particles
Particle* particles;
// The number of particles
int numParticles;
// The spawn rate
float spawnRate;
// When should the next particle be spawned?
float nextSpawn;
ParticleSystem(int maxParticles, const VertexStructure& structure ) {
particles = new Particle[maxParticles];
numParticles = maxParticles;
for (int i = 0; i < maxParticles; i++) {
particles[i].init(structure);
}
spawnRate = 0.05f;
nextSpawn = spawnRate;
position = vec3(0.5f, 1.3f, 0.5f);
float b = 0.1f;
emitMin = position + vec3(-b, -b, -b);
emitMax = position + vec3(b, b, b);
}
void update(float deltaTime) {
// Do we need to spawn a particle?
nextSpawn -= deltaTime;
bool spawnParticle = false;
if (nextSpawn < 0) {
spawnParticle = true;
nextSpawn = spawnRate;
}
for (int i = 0; i < numParticles; i++) {
if (particles[i].dead) {
if (spawnParticle) {
EmitParticle(i);
spawnParticle = false;
}
}
particles[i].Integrate(deltaTime);
}
}
void render(TextureUnit tex, Texture* image, ConstantLocation mLocation, mat4 V) {
Graphics::setBlendingMode(BlendingOperation::SourceAlpha, BlendingOperation::InverseSourceAlpha);
Graphics::setRenderState(RenderState::DepthWrite, false);
/************************************************************************/
/* Exercise 7 1.1 */
/************************************************************************/
/* Change the matrix V in such a way that the billboards are oriented towards the camera */
/************************************************************************/
/* Exercise 7 1.2 */
/************************************************************************/
/* Animate using at least one new control parameter */
for (int i = 0; i < numParticles; i++) {
// Skip dead particles
if (particles[i].dead) continue;
Graphics::setMatrix(mLocation, particles[i].M * V);
particles[i].render(tex, image);
}
Graphics::setRenderState(RenderState::DepthWrite, true);
}
float getRandom(float minValue, float maxValue) {
int randMax = 1000000;
int randInt = Random::get(0, randMax);
float r = (float) randInt / (float) randMax;
return minValue + r * (maxValue - minValue);
}
void EmitParticle(int index) {
// Calculate a random position inside the box
float x = getRandom(emitMin.x(), emitMax.x());
float y = getRandom(emitMin.y(), emitMax.y());
float z = getRandom(emitMin.z(), emitMax.z());
vec3 pos;
pos.set(x, y, z);
vec3 velocity(0, 0.3f, 0);
particles[index].Emit(pos, velocity, 3.0f);
}
};
namespace {
const int width = 1024;
const int height = 768;
double startTime;
Shader* vertexShader;
Shader* fragmentShader;
Program* program;
float angle = 0.0f;
// null terminated array of MeshObject pointers
MeshObject* objects[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
// null terminated array of PhysicsObject pointers
PhysicsObject* physicsObjects[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
// The view projection matrix aka the camera
mat4 P;
mat4 View;
mat4 PV;
vec3 cameraPosition;
MeshObject* sphere;
PhysicsObject* po;
PhysicsWorld physics;
// uniform locations - add more as you see fit
TextureUnit tex;
ConstantLocation pvLocation;
ConstantLocation mLocation;
ConstantLocation tintLocation;
Texture* particleImage;
ParticleSystem* particleSystem;
double lastTime;
void update() {
double t = System::time() - startTime;
double deltaT = t - lastTime;
//Kore::log(Info, "%f\n", deltaT);
lastTime = t;
Kore::Audio::update();
Graphics::begin();
Graphics::clear(Graphics::ClearColorFlag | Graphics::ClearDepthFlag, 0xff9999FF, 1000.0f);
Graphics::setFloat4(tintLocation, vec4(1, 1, 1, 1));
program->set();
angle += 0.3f * deltaT;
float x = 0 + 3 * Kore::cos(angle);
float z = 0 + 3 * Kore::sin(angle);
cameraPosition.set(x, 2, z);
//PV = mat4::Perspective(60, (float)width / (float)height, 0.1f, 100) * mat4::lookAt(vec3(0, 2, -3), vec3(0, 2, 0), vec3(0, 1, 0));
P = mat4::Perspective(60, (float)width / (float)height, 0.1f, 100);
View = mat4::lookAt(vec3(x, 2, z), vec3(0, 2, 0), vec3(0, 1, 0));
PV = P * View;
Graphics::setMatrix(pvLocation, PV);
// iterate the MeshObjects
MeshObject** current = &objects[0];
while (*current != nullptr) {
// set the model matrix
Graphics::setMatrix(mLocation, (*current)->M);
(*current)->render(tex);
++current;
}
// Update the physics
physics.Update(deltaT);
PhysicsObject** currentP = &physics.physicsObjects[0];
while (*currentP != nullptr) {
(*currentP)->UpdateMatrix();
Graphics::setMatrix(mLocation, (*currentP)->Mesh->M);
(*currentP)->Mesh->render(tex);
++currentP;
}
particleSystem->update(deltaT);
particleSystem->render(tex, particleImage, mLocation, View);
Graphics::end();
Graphics::swapBuffers();
}
void SpawnSphere(vec3 Position, vec3 Velocity) {
PhysicsObject* po = new PhysicsObject();
po->SetPosition(Position);
po->Velocity = Velocity;
po->Collider.radius = 0.2f;
po->Mass = 5;
po->Mesh = sphere;
// The impulse should carry the object forward
// Use the inverse of the view matrix
po->ApplyImpulse(Velocity);
physics.AddObject(po);
}
void keyDown(KeyCode code, wchar_t character) {
if (code == Key_Space) {
// The impulse should carry the object forward
// Use the inverse of the view matrix
vec4 impulse(0, 0.4, 2, 0);
mat4 viewI = View;
viewI.Invert();
impulse = viewI * impulse;
vec3 impulse3(impulse.x(), impulse.y(), impulse.z());
SpawnSphere(cameraPosition + impulse3 *0.2f, impulse3);
}
}
void keyUp(KeyCode code, wchar_t character) {
if (code == Key_Left) {
// ...
}
}
void mouseMove(int x, int y, int movementX, int movementY) {
}
void mousePress(int button, int x, int y) {
}
void mouseRelease(int button, int x, int y) {
}
void init() {
FileReader vs("shader.vert");
FileReader fs("shader.frag");
vertexShader = new Shader(vs.readAll(), vs.size(), VertexShader);
fragmentShader = new Shader(fs.readAll(), fs.size(), FragmentShader);
// This defines the structure of your Vertex Buffer
VertexStructure structure;
structure.add("pos", Float3VertexData);
structure.add("tex", Float2VertexData);
structure.add("nor", Float3VertexData);
program = new Program;
program->setVertexShader(vertexShader);
program->setFragmentShader(fragmentShader);
program->link(structure);
tex = program->getTextureUnit("tex");
pvLocation = program->getConstantLocation("PV");
mLocation = program->getConstantLocation("M");
tintLocation = program->getConstantLocation("tint");
objects[0] = new MeshObject("Base.obj", "Level/basicTiles6x6.png", structure);
objects[0]->M = mat4::Translation(0.0f, 1.0f, 0.0f);
sphere = new MeshObject("ball_at_origin.obj", "Level/unshaded.png", structure);
SpawnSphere(vec3(0, 2, 0), vec3(0, 0, 0));
Graphics::setRenderState(DepthTest, true);
Graphics::setRenderState(DepthTestCompare, ZCompareLess);
Graphics::setTextureAddressing(tex, U, Repeat);
Graphics::setTextureAddressing(tex, V, Repeat);
particleImage = new Texture("SuperParticle.png", true);
particleSystem = new ParticleSystem(100, structure);
}
}
int kore(int argc, char** argv) {
Application* app = new Application(argc, argv, width, height, 0, false, "Exercise7");
init();
app->setCallback(update);
startTime = System::time();
lastTime = 0.0f;
Kore::Mixer::init();
Kore::Audio::init();
Keyboard::the()->KeyDown = keyDown;
Keyboard::the()->KeyUp = keyUp;
Mouse::the()->Move = mouseMove;
Mouse::the()->Press = mousePress;
Mouse::the()->Release = mouseRelease;
app->start();
delete app;
return 0;
}
There's a comment where the teacher wants us to add the code.
The variable for the view matrix "View" is in "namespace". I've only ever used namespace as a library but this one doesn't have a name. So how do I use it?
The comment says that we should use matrix V. So I just add V = Inverse View Matrix * Model Matrix to the code and it removes the rotation?
I'm sorry for the stupid questions, it's supposed to be a class for beginners but it's really anything but. The lecture notes aren't very helpful when it comes to the programming part and I only found tutorials for OpenGL or Unity or Direct X and where not using any of it.
Please help me, I need to hand this in until Saturday morning and I've already spent the last two days trying out code and I've got nothing so far!
You can find the whole thing here: https://github.com/TUDGameTechnology/Exercise7
You don't have to do anything special to access an unnamed namespace. This thread explains more.
You are most probably trying to reference View within methods that cannot see your namespace because of the order in which they are defined in your file.
This line in your update method:
particleSystem->render(tex, particleImage, mLocation, View);
is already passing View into the render method.
void render(TextureUnit tex, Texture* image, ConstantLocation mLocation, mat4 V)
That means that in this case mat4 v is your camera view.
I want to rotate my triangle in OpenGL, and running program on Raspberry Pi.
I can draw triangle and move it.
But I have no idea to rotate it..
Nothing rotates.
#include <cstdio>
#include <ctime>
#include <cmath>
#include <string>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES/gl.h>
#include <bcm_host.h>
EGLDisplay Disp;
EGLSurface Surface;
EGLContext Context;
int ScrWidth, ScrHeight;
float MVPMatrix[16];
float ProjectionMatrix[16];
float ViewMatrix[16];
using namespace std;
class Shader
{
private:
string VertexShaderFile;
string FragmentShaderFile;
GLuint Load(GLenum type, string FileName)
{
(Compile shader)
}
GLuint Program;
bool Linked;
public:
Shader(string FileNameV, string FileNameF)
{
Linked = false;
VertexShaderFile = FileNameV;
FragmentShaderFile = FileNameF;
}
bool Load()
{
(Link vertex/fragment shader)
}
void Use()
{
glUseProgram(Program);
}
int GetAttrLoc(const char *Name)
{
glGetAttribLocation(Program, Name);
}
int GetUniformLoc(const char *Name)
{
return glGetUniformLocation(Program, Name);
}
~Shader()
{
if(Linked)
{
Linked = false;
glDeleteProgram(Program);
}
}
};
class Triangle
{
private:
const int COORDS_PER_VERTEX = 3;
const int vertexCount = 9 / COORDS_PER_VERTEX; //9: Length of triangleCoords
const int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
static float TriangleCoords [];
float Color[4];
float XOff;
float YOff;
float ZOff;
Shader *S;
public:
Triangle()
{
XOff = YOff = ZOff = 0;
S = new Shader("Shaders/test.vsh", "Shaders/test.fsh");
if (!S->Load())
{
delete S;
S = NULL;
}
}
void SetColor(int R, int G, int B, int A)
{
Color[0] = R / 255.0;
Color[1] = G / 255.0;
Color[2] = B / 255.0;
Color[3] = A / 255.0;
}
void SetXYZ(int X, int Y, int Z)
{
(Sets position)
}
bool Draw()
{
float TriangleCoords[] = { // in counterclockwise order:
-0.0 + XOff, 0.622008459 + YOff, 0.0 + ZOff, // top
-0.5 + XOff, -0.311004243 + YOff, 0.0 + ZOff, // bottom left
0.5 + XOff, -0.311004243 + YOff, 0.0 + ZOff // bottom right
};
printf("%f\n", TriangleCoords[1]);
//glMatrixMode(GL_PROJECTION);
if (S == NULL)
return false;
S->Use();
// Load the vertex data
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, TriangleCoords);
// get handle to shape's transformation matrix
// Pass the projection and view transformation to the shader
//UniformMatrix4fv(S->GetUniformLoc("uMVPMatrix"), 1, false, MVPMatrix);
glUniform4fv(S->GetUniformLoc("vColor"), 1, Color);
glEnableVertexAttribArray(0);
//glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
float X = LocalTime->tm_hour / 23.0;
float Y = LocalTime->tm_min / 59.0;
float Z = LocalTime->tm_sec / 59.0;
glTranslatef(0, 0, 1);
glRotatef(60, 1.f, 0.f, 0.f);
glRotatef(30, 0.f, 1.f, 0.f);
glRotatef(30, 0.f, 0.f, 1.f);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glPopMatrix();
return true;
}
};
bool InitDisplay()
{
bcm_host_init();
Disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(eglInitialize(Disp, NULL, NULL) != EGL_TRUE)
{
printf("Display initialize error.\n");
return false;
}
printf("Display initialized.\n");
static const EGLint AttrList[] =
{
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
EGLConfig Config;
int ConfigCount;
if(eglChooseConfig(Disp, AttrList, &Config, 1, &ConfigCount) != EGL_TRUE)
{
printf("Display choose config error.\n");
return false;
}
printf("Display config chosen. %d configs.\n", ConfigCount);
//if(eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE)
//{
// printf("Bind API error.\n");
// return false;
//}
//printf("API bound.\n");
static const EGLint ContextAttr[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
if((Context = eglCreateContext(Disp, Config, EGL_NO_CONTEXT, ContextAttr)) == EGL_NO_CONTEXT)
{
printf("Create context error.\n");
return false;
}
printf("Context created.\n");
if(graphics_get_display_size(0 /* LCD */, &ScrWidth, &ScrHeight) < 0)
{
printf("Get screen size error.\n");
return false;
}
printf("Got screen size. %dx%d\n", ScrWidth, ScrHeight);
DISPMANX_DISPLAY_HANDLE_T DispmanDisp;
DispmanDisp = vc_dispmanx_display_open(0 /* LCD */);
printf("Dispmanx - Display opened.\n");
DISPMANX_UPDATE_HANDLE_T DispmanUpdate;
DispmanUpdate = vc_dispmanx_update_start(0);
printf("Dispmanx - Update started.\n");
DISPMANX_ELEMENT_HANDLE_T DispmanElement;
VC_RECT_T DestRect;
VC_RECT_T SrcRect;
DestRect.x = 0;
DestRect.y = 0;
DestRect.width = ScrWidth;
DestRect.height = ScrHeight;
SrcRect.x = 0;
SrcRect.y = 0;
SrcRect.width = ScrWidth << 16;
SrcRect.height = ScrHeight << 16;
DispmanElement= vc_dispmanx_element_add(
DispmanUpdate,
DispmanDisp,
0/*layer*/,
&DestRect,
0/*src*/,
&SrcRect,
DISPMANX_PROTECTION_NONE,
0 /*alpha*/,
0/*clamp*/,
0/*transform*/
);
printf("Dispmanx - Element added.\n");
static EGL_DISPMANX_WINDOW_T NativeWindow;
NativeWindow.element = DispmanElement;
NativeWindow.width = ScrWidth;
NativeWindow.height = ScrHeight;
vc_dispmanx_update_submit_sync(DispmanUpdate);
printf("Dispmanx - Sync submited.\n");
if((Surface = eglCreateWindowSurface(Disp, Config, &NativeWindow, NULL)) == EGL_NO_SURFACE)
{
printf("Create surface error.\n");
return false;
}
printf("Surface created\n");
if(eglMakeCurrent(Disp, Surface, Surface, Context) != EGL_TRUE)
{
printf("Make onnection between context and surface error.\n");
return false;
}
printf("Connection made between context and surface.\n");
glEnable(GL_CULL_FACE);
glMatrixMode(GL_MODELVIEW);
printf("Graphics system ready.\n");
return true;
}
void makeFrustum(float fovY, float aspectRatio, float front, float back)
{
const float DEG2RAD = 3.14159265 / 180;
float tangent = tan(fovY / 2 * DEG2RAD); // tangent of half fovY
float height = front * tangent; // half height of near plane
float width = height * aspectRatio; // half width of near plane
// params: left, right, bottom, top, near, far
glFrustumf(-width, width, -height, height, front, back);
}
void DrawLoop()
{
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glViewport(0, 0, ScrWidth, ScrHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
makeFrustum(45.0, ScrWidth / (float)ScrHeight, 1, 500);
glEnableClientState(GL_VERTEX_ARRAY);
Triangle T1;
Triangle T2;
Triangle T3;
Triangle T4;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.f, 0.f, -50.f);
while (1)
{
time_t Time;
time(&Time);
tm *LocalTime = localtime(&Time);
printf("%d:%d:%d\n", LocalTime->tm_hour, LocalTime->tm_min, LocalTime->tm_sec);
float R = LocalTime->tm_hour / 23.0;
float G = LocalTime->tm_min / 59.0;
float B = LocalTime->tm_sec / 59.0;
T1.SetColor(255, 0, 0, 255);
T1.SetXYZ(B * ScrWidth, B * ScrHeight, 0);
//glClearColor(0, 0, 0, 1.0);
//glClear(GL_COLOR_BUFFER_BIT);
if (!T1.Draw() || !T2.Draw() || !T3.Draw() || !T4.Draw())
{
return;
}
glFlush();
eglSwapBuffers(Disp, Surface);
}
}
int main()
{
if(!InitDisplay())
{
printf("Display initialize error.\n");
return false;
}
DrawLoop();
return 0;
}
What should I do to rotate triangle?
I referenced working code, but it still doesn't rotates.
You're trying to use OpenGL ES 1.1 functions (glLoadIdentity, glMatrixMode, glTranslatef, glRotatef, etc) in an OpenGL ES 2.0 context - this won't work. You can use either OpenGL ES 1.1 OR OpenGL ES 2.0, but you can't use both at the same time from the same context.
I would suggest sticking with OpenGL ES 2.0 using shaders, and learning the OpenGL ES 2.0 way of doing things, as this is how all of the newer APIs work.
To do translation and rotation you need to encode it into an MVP matrix and pass this as a uniform to the vertex shader which you use when calculating gl_Position. Some examples here:
https://open.gl/transformations
I'm writing a program to graph 3D surfaces. It uses CUDA to do rendering and then OpenGL to display the results. It works fine and renders pretty and accurate results, but it does not correctly update the window. Here's my program in pseudocode:
void display() {
//render stuff
glutSwapBuffers();
glutPostRedisplay();
}
int main() {
//set up OpenGL and CUDA
glutMainLoop();
return 0;
}
This should update continuously, but it doesn't. It correctly calls the display method and CUDA renders the results, but it does not display the results in the window until the window is resized. So it calls the display function something like 30 times a second, but the window doesn't actually show the results until resized or minimized.
Here's my entire program. It's EXTREMELY messy and has no comments since I was learning OpenGL as I went along.
#define GL_GLEXT_PROTOTYPES
#include <GL/freeglut_std.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <stdlib.h>
#include <stdio.h>
#include <cuda_gl_interop.h>
#define XSIZE 1280
#define YSIZE 640
float theta = .15;
float phi = 1;
float r = 10;
float3 lightDirection = make_float3(9, 5, -5);
float delta = .001;
GLuint pbo; // OpenGL pixel buffer object
struct cudaGraphicsResource *cuda_pbo_resource; // handles OpenGL-CUDA exchange
GLuint texid; // Texture
GLuint shader;
__host__ __device__ float3 operator+(float3 a, float3 b) {
return make_float3(a.x + b.x, a.y + b.y, a.z + b.z);
}
__host__ __device__ float3 operator-(float3 a, float3 b) {
return make_float3(a.x - b.x, a.y - b.y, a.z - b.z);
}
__host__ __device__ float3 operator*(float3 a, float b) {
return make_float3(a.x * b, a.y * b, a.z * b);
}
__host__ __device__ float3 operator/(float3 a, float b) {
return make_float3(a.x / b, a.y / b, a.z / b);
}
__host__ __device__ float operator*(float3 a, float3 b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
__host__ __device__ float magnitude(float3 a) {
return std::sqrt(a * a);
}
__host__ __device__ float3 normalize(float3 a) {
return a / magnitude(a);
}
__device__ float valueAt(float3 a) {
return std::sin(a.x) + std::sin(a.y) + std::sin(a.z);
}
__device__ float3 normalAt(float3 a) {
float x = valueAt(make_float3(a.x + .001, a.y, a.z)) - valueAt(a);
float y = valueAt(make_float3(a.x, a.y + .001, a.z)) - valueAt(a);
float z = valueAt(make_float3(a.x, a.y, a.z + .001)) - valueAt(a);
return make_float3(x, y, z) / .001;
}
__device__ float estimateDistance(float3 a) {
return std::abs(valueAt(a) / magnitude(normalAt(a)));
}
__device__ float3 trace(float3 from, float3 direction) {
float totalDistance = 0;
float3 v = from;
for(int steps = 0; steps < 256; steps++) {
if(magnitude(v) < 5) break;
totalDistance += 1;
v = from + direction * totalDistance;
}
for(int steps = 0; steps < 256; steps++) {
v = from + direction * totalDistance;
float distance = estimateDistance(v);
totalDistance += distance * .1;
if(distance < .1 && magnitude(v) < 5) return v;
}
return make_float3(0, 0, 0);
}
__device__ uchar4 colorAt(float3 v, float3 lightDirection, float3 viewDirection) {
float3 normal = normalize(normalAt(v));
float3 h = normalize(viewDirection + lightDirection);
float specular = std::abs(std::pow(h * normal, 15)) * .5;
float diffuse = std::abs(lightDirection * normal) * .5;
float lighting = (specular + diffuse) * .9;
lighting += .1;
float3 color;
if((fmod(v.x + 1000, 1) < .5 && fmod(v.y + 1000, 1) < .5) || (fmod(v.x + 1000, 1) > .5 && fmod(v.y + 1000, 1) > .5)) {
color.x = 1;
color.y = .5;
color.z = .25;
}
else {
color.x = .75;
color.y = .5;
color.z = .25;
}
color = color * lighting;
return make_uchar4((unsigned char)(color.x * 255), (unsigned char)(color.y * 255), (unsigned char)(color.z * 255), 255);
}
__global__ void eval(float3 position, float3 direction, float3 right, float3 up, float3 lightDirection, float delta, uchar4* dev_pixels) {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int offset = x + y * XSIZE;
float rAngle = (x - XSIZE / 2) * delta;
float uAngle = (-y + YSIZE / 2) * delta;
float3 rayDirection = direction + right * rAngle + up * uAngle;
rayDirection = normalize(rayDirection);
float3 v = trace(position, rayDirection);
uchar4 color;
if(magnitude(v) != 0) color = colorAt(v, lightDirection, direction);
dev_pixels[offset] = color;
}
void display() {
uchar4* dev_pixels;
cudaGLRegisterBufferObject(pbo);
cudaGLMapBufferObject((void**)&dev_pixels, pbo);
dim3 dim_block(16, 16);
dim3 dim_grid(XSIZE / 16, YSIZE / 16);
float3 position = make_float3(std::cos(theta) * std::sin(phi) * r, std::sin(theta) * std::sin(phi) * r, std::cos(phi) * r);
float3 direction = normalize(position) * -1;
float3 right = make_float3(-std::sin(theta), std::cos(theta), 0);
right = normalize(right);
float3 up = make_float3(-std::cos(phi) * std::cos(theta), -std::cos(phi) * std::sin(theta), std::sin(phi));
up = normalize(up);
eval<<<dim_grid, dim_block>>>(position, direction, right, up, lightDirection, delta, dev_pixels);
cudaGLUnmapBufferObject(pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBindTexture(GL_TEXTURE_2D, texid);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, XSIZE, YSIZE, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,1.0f); glVertex3f(0.0f,0.0f,0.0f);
glTexCoord2f(0.0f,0.0f); glVertex3f(0.0f,1.0f,0.0f);
glTexCoord2f(1.0f,0.0f); glVertex3f(1.0f,1.0f,0.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(1.0f,0.0f,0.0f);
glEnd();
glutSwapBuffers();
theta += .01;
glutPostRedisplay();
}
int main(int argc, char **argv) {
lightDirection = normalize(lightDirection);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(XSIZE, YSIZE);
glutCreateWindow("Grapher");
glutDisplayFunc(display);
glViewport(0, 0, XSIZE, YSIZE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1.0f, 0, 1.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.5f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, XSIZE * YSIZE * 4, NULL, GL_DYNAMIC_COPY);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, XSIZE, YSIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glutMainLoop();
return 0;
}
I'm using NSight Eclipse on Ubuntu 12.04.
At the end of your display routine, add these 2 lines:
...
glTexCoord2f(1.0f,1.0f); glVertex3f(1.0f,0.0f,0.0f);
glEnd();
glutSwapBuffers();
glClear(GL_DEPTH_BUFFER_BIT); // add this line
cudaGLUnregisterBufferObject(pbo); // add this line
theta += .01;
glutPostRedisplay();
...
The first addition allows the animation to proceed frame by frame. (Frames after the first will render properly.)
The second addition makes it so that if you close the animation window, you won't get a seg fault. cudaGLUnregisterBufferObject must be called on any previously registered object before the underlying OpenGL resource gets freed (or else you get a seg fault). When you click the X to close the window, the OpenGL buffer object (pbo) gets freed as the OpenGL context disappears.