I just ran my first java program running JOGL. I have worked on c++ using the Glut.h library. I have a question as I am trying to understand the coordinate system on the graphic window displayed using JOGL.
Now, in c++, the window coordinate system begins at the top left corner of the window (that is where the origin is). However, when I run the below JOGL program, the origin is not at the top left corner. The below program will draw 3 lines, all of which start at the origin:
import com.sun.opengl.util.Animator;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
public class Lab2 implements GLEventListener {
public static void main(String[] args) {
Frame frame = new Frame("My First JOGL Project");
GLCanvas canvas = new GLCanvas();
canvas.addGLEventListener(new Lab2());
frame.add(canvas);
frame.setSize(640, 480);
final Animator animator = new Animator(canvas);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
// Run this on another thread than the AWT event queue to
// make sure the call to Animator.stop() completes before
// exiting
new Thread(new Runnable() {
public void run() {
animator.stop();
System.exit(0);
}
}).start();
}
});
// Center frame
frame.setLocationRelativeTo(null);
frame.setVisible(true);
animator.start();
}
public void init(GLAutoDrawable drawable) {
// Use debug pipeline
// drawable.setGL(new DebugGL(drawable.getGL()));
GL gl = drawable.getGL();
System.err.println("INIT GL IS: " + gl.getClass().getName());
// Enable VSync
gl.setSwapInterval(1);
// Setup the drawing area and shading mode
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glShadeModel(GL.GL_SMOOTH); // try setting this to GL_FLAT and see what happens.
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
GL gl = drawable.getGL();
GLU glu = new GLU();
if (height <= 0) { // avoid a divide by zero error!
height = 1;
}
final float h = (float) width / (float) height;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
//glu.gluPerspective(45.0f, h, 1.0, 20.0);
glu.gluPerspective(45.0f, h, 2.0, 20.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
// Clear the drawing area
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
// Reset the current matrix to the "identity"
gl.glLoadIdentity();
// Move the "drawing cursor" around
gl.glTranslatef(-1.5f, 0.0f, -6.0f);
// Drawing Lines
gl.glBegin(GL.GL_LINES);
gl.glColor3f(0.0f, 1.0f, 0.0f); // Set the current drawing color to green
gl.glVertex3f(0.0f, 0.0f, 0.0f); //Set first vertex of first line
gl.glVertex3f(1.0f, 0.0f, 0.0f); //Set second vertex of first line
gl.glColor3f(0.0f, 0.0f, 1.0f); // Set the current drawing color to blue
gl.glVertex3f(0.0f, 0.0f, 0.0f); //Set first vertex of second line
gl.glVertex3f(0.0f, 1.0f, 0.0f); //Set second vertex of second line
gl.glColor3f(1.0f, 0.0f, 0.0f); // Set the current drawing color to red
gl.glVertex3f(0.0f, 0.0f, 0.0f); //Set first vertex of third line
gl.glVertex3f(0.0f, 0.0f, 1.0f); //Set second vertex of third line
gl.glEnd();
// Flush all drawing operations to the graphics card
gl.glFlush();
}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean
deviceChanged) {}
}
Can someone epxlain?
Related
I'm trying to render 2D text over a 3D scene. The 2D text is loaded using freetype from a TTF font and uses an orthographic projection to render and the scene uses a perspective projection using my camera. I have modified the code from this Learn OpenGL tutorial for text rendering. I can render the text by itself and the 3D scene separately however the 2D text does not appear when drawing them together.
My render function:
void Engine::render()
{
std::string fpsStr = std::to_string(fps).substr(0, std::to_string(fps).find(".") + 3);
glViewport(0, 0, surface_width, surface_height);
glClearColor(0.53f, 0.8f, 0.92f, 1.0f);
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 3D scene gets rendered here
scene->render(display, surface, deltaTime);
//
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Text gets rendered here
debugText.renderText(fpsStr,25.0f, 25.0f, 1.0f, glm::vec3(0.0, 0.0, 0.0));
//
glDisable(GL_BLEND);
eglSwapBuffers(display, surface);
}
The text projection is a member variable (glm::mat4) that is initialized during creation of the text rendering class like so:
...
projection = glm::ortho(0.0f, static_cast<float>(screenWidth), 0.0f, static_cast<float>(screenHeight));
...
My render text function:
void Font::renderText(std::string text, float x, float y, float scale, glm::vec3 colour)
{
// activate corresponding render state
textShader.use();
textShader.setMat4("projection", projection);
textShader.setVec3("textColor", colour);
glActiveTexture(GL_TEXTURE0);
// iterate through all characters
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++)
{
Character ch = characters[*c];
float xpos = x + ch.bearing.x * scale;
float ypos = y - (ch.size.y - ch.bearing.y) * scale;
float w = ch.size.x * scale;
float h = ch.size.y * scale;
// update VBO for each character
float vertices[6][4] = {
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos, ypos, 0.0f, 1.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos + w, ypos + h, 1.0f, 0.0f }
};
// render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.textureID);
// update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64)
}
glBindTexture(GL_TEXTURE_2D, 0);
}
Here are two images, in this one I'm only rendering the text and in this one I've enabled both the 3D scene and the text, however only the 3D scene is displayed.
How can I overlay this 2D perspective over the 3D scene so they both get rendered?
You have said that rendering them (the 2D quad and 3D scene) separately works fine but rendering them together works causes the 2D quad not to render. Hmmm, try checking your rendering order of the objects; make sure you are binding and unbinding your shaders correctly.
Is there a particular reason you have disabled depth testing for the text (try enabling it and see if that fixes the problem) ?
Here's the vertex buffer information of the quad I'm drawing:
static const GLfloat pv_quad[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
This quad is used to draw 2D frames on the screen as part of the graphical user interface. The class I use to do this is Mage::Interface::Frame. I'll spare you the header definition and instead give you the class's implementation, as it's small. There's some test code in here, so ignore the fact the shader is part of the class. I know it shouldn't be there.
#include <Mage/Root.h>
#include <Mage/Interface/Frame.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
using Mage::Interface::Frame;
Frame::Frame()
: width(300), height(200), position(0, 0), color(1.0, 1.0, 1.0), model(1.0), rotation(0) {
prog.compileFile("Data/Shaders/FrameVertex.glsl", Mage::ShaderType::VERTEX);
prog.compileFile("Data/Shaders/FrameFragment.glsl", Mage::ShaderType::FRAGMENT);
prog.link();
this->calcTransform();
}
void Frame::setSize(int w, int h) {
this->width = w;
this->height = h;
this->calcTransform();
}
void Frame::setColor(int r, int g, int b) {
this->color = glm::vec3(float(r) / 256, float(g) / 256, float(b) / 256);
}
void Frame::setRotation(float degrees) {
this->rotation = glm::radians(degrees);
this->calcTransform();
}
void Frame::calcTransform() {
this->model = glm::mat4(1.0f); // reset model to origin.
// 1280 and 720 are the viewport's size. This is only hard coded for tests.
this->model = glm::scale(this->model, glm::vec3(float(width) / 1280, float(height) / 720, 1.0f));
this->model = glm::rotate(this->model, this->rotation, glm::vec3(0.0f, 0.0f, 1.0f));
this->model = glm::translate(this->model, glm::vec3(position.x, position.y, 0.0f));
}
void Frame::draw() {
Mage::VertexObject obj = ROOT.getRenderWindow()->getVertexBufferObject()->getObject("PrimitiveQuad");
prog.use();
prog.setUniform("mvp", this->model);
prog.setUniform("fColor", this->color);
glEnableVertexAttribArray(0);
ROOT.getRenderWindow()->getVertexBufferObject()->bind();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)obj.begin);
glDrawArrays(GL_TRIANGLE_STRIP, 0, obj.size);
glDisableVertexAttribArray(0);
}
Here's the drawing function that's called every frame:
void RenderWindow::render() {
Mage::Interface::Frame F;
F.setSize(400, 200);
F.setRotation(0);
while (glfwWindowShouldClose(this->win) == 0) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
F.draw();
glfwSwapBuffers(this->win);
glfwPollEvents();
}
}
When I have setRotation(0), the resulting quad is indeed, 400 pixels wide and 200 pixels high, right in the centre of my screen as you would expect.
However, if I set the rotation to (90), well, this happens:
As you can see, that's not at all close to a 90 degrees turn. It should be 400px high and 200px wide.
Anyone care to explain what's going on here?
EDIT: Some playing around has shown me that the problem is with the scale, not the rotation. When I comment out the scale, the rotation appears to be correct.
The angle argument to glm::rotate() is in radians, not degrees:
m: Input matrix multiplied by this rotation matrix.
angle: Rotation angle expressed in radians.
axis: Rotation axis, recommanded [sic] to be normalized.
Use this:
void Frame::setRotation(float degrees) {
this->rotation = glm::radians( degrees );
this->calcTransform();
}
I am assuming that this game is supposed to be a 3D game with a 2D GUI, although this was not specified in the question, though not entirely necessary, as my answer will be the same.
When rendering with a 3D matrix, using a perspective view (Field of View taken into account), as opposed to using an orthographic view, the shapes will bend to their position depending on the fov.
So with that, I propose that you use a simple solution, and initialize a 2D viewing matrix (or orthographic matrix) for your 2D interface. If you are just looking for a simple way to render a 2D quad onto the screen freeGLUT(free Graphics Library Utility Toolkit) is there for you. There are plenty of docs out there to help install freeglut, so once you finish that, initialize a 2D rendering matrix, then render the quad using glVertex2i/f or glVertex3i/f, like so:
void setView2d()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, *SCREEN_WIDTH, *SCREEN_HEIGHT, 0);
glMatrixMode( GL_MODELVIEW );
glDisable(GL_DEPTH_TEST);
glLoadIdentity();
}
void setView3d()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70, (GL_FLOAT)*SCREEN_WIDTH / *SCREEN_HEIGHT, 0.1, 100);
glEnable(GL_DEPTH_TEST);
glLoadIdentity();
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_TEST);
setView2d(); //Render 2D objects
glPushMatrix();
{
//glTranslatef() and glRotatef() still work for 2D
//if using rotate, rotate on z axis, like so:
glRotatef(90, 0, 0, 1);
glBegin(GL_TRIANGLES);
{
glVertex2i(0, 0);
glVertex2i(100, 0);
glVertex2i(0, 100);
/*
glVertex2i is replacable with glVertex2f, glVertex3i, and glVertex3f
if using a glVertex3, set the z value to 0
*/
}
glEnd();
}
glPopMatrix();
setView3d(); //Render 3D objects
glPushMatrix();
{
//render 3D stuff
}
glPopMatrix();
glutSwapBuffers();
}
I should also mention that when using the gluOrtho2D, coordinates used in vertex x,y are based on pixels, instead of the 3D blocks.
Hope this helped,
-Nick
My 3D world draws perfectly every time but the 2D text never draws. The code below features my latest effort using a tutorial from lighthouse3D. I get the feeling its something stupidly simple and im just not seeing it.
Rendering code :
void ScreenGame::draw(SDL_Window * window)
{
glClearColor(0.5f,0.5f,0.5f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set up projection matrix
glm::mat4 projection(1.0);
projection = glm::perspective(60.0f,800.0f/600.0f,1.0f,150.0f);
rt3d::setUniformMatrix4fv(shaderProgram, "projection", glm::value_ptr(projection));
GLfloat scale(1.0f); // just to allow easy scaling of complete scene
glm::mat4 modelview(1.0); // set base position for scene
mvStack.push(modelview);
mvStack.top() = glm::lookAt(camera->getEye(),camera->getAt(),camera->getUp());
glm::vec4 tmp = mvStack.top()*lightPos;
light0.position[0] = tmp.x;
light0.position[1] = tmp.y;
light0.position[2] = tmp.z;
rt3d::setLightPos(shaderProgram, glm::value_ptr(tmp));
glUseProgram(skyBoxShader); // Switch shaders, reset uniforms for skybox
rt3d::setUniformMatrix4fv(skyBoxShader, "projection", glm::value_ptr(projection));
glDepthMask(GL_FALSE); // make sure depth test is off
glm::mat3 mvRotOnlyMat3 = glm::mat3(mvStack.top());
mvStack.push( glm::mat4(mvRotOnlyMat3) );
skyBox->draw(mvStack); // drawing skybox
mvStack.pop();
glDepthMask(GL_TRUE); // make sure depth test is on
mvStack.top() = glm::lookAt(camera->getEye(),camera->getAt(),camera->getUp());
glUseProgram(shaderProgram); // Switch back to normal shader program
rt3d::setUniformMatrix4fv(shaderProgram, "projection", glm::value_ptr(projection));
rt3d::setLightPos(shaderProgram, glm::value_ptr(tmp));
rt3d::setLight(shaderProgram, light0);
// Draw all visible objects...
Ball->draw(mvStack);
ground->draw(mvStack);
building1->draw(mvStack);
building2->draw(mvStack);
setOrthographicProjection();
glPushMatrix();
glLoadIdentity();
renderBitmapString(5,30,1,GLUT_BITMAP_HELVETICA_18,"Text Test");
glPopMatrix();
restorePerspectiveProjection();
SDL_GL_SwapWindow(window); // swap buffers
}
using the following methods :
void setOrthographicProjection() {
// switch to projection mode
glMatrixMode(GL_PROJECTION);
// save previous matrix which contains the
//settings for the perspective projection
glPushMatrix();
// reset matrix
glLoadIdentity();
// set a 2D orthographic projection
glOrtho(0.0F, 800, 600, 0.0F, -1.0F, 1.0F);
// switch back to modelview mode
glMatrixMode(GL_MODELVIEW);
}
void restorePerspectiveProjection() {
glMatrixMode(GL_PROJECTION);
// restore previous projection matrix
glPopMatrix();
// get back to modelview mode
glMatrixMode(GL_MODELVIEW);
}
void renderBitmapString(
float x,
float y,
int spacing,
void *font,
char *string) {
char *c;
int x1=x;
for (c=string; *c != '\0'; c++) {
glRasterPos2f(x1,y);
glutBitmapCharacter(font, *c);
x1 = x1 + glutBitmapWidth(font,*c) + spacing;
}
}
Im trying to draw a triangle on screen using openGL on cocos2dx. Currently i have subclassed cocos' Node object inside which i do my drawing.
In the latest version of cocos2dx i cannot override the draw() function instead im trying to override draw(Renderer* renderer, const Mat4 &transform, uint32_t flags) and add Custom commands to the renderer, like so.
const GLfloat vertices[9] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
void MyNodeSubclass::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags){
CustomCommand *_customCommand = new CustomCommand();
_customCommand->init(_globalZOrder);
_customCommand->func = CC_CALLBACK_0(MyNodeSubclass::drawTriangle, this);
renderer->addCommand(_customCommand);
}
void MyNodeSubclass::drawTriangle(){
glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE,0, vertices);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
And My Load Shader method
void MyNodeSubclass::loadOurShaders(){
GLProgram* myShader = new GLProgram();
myShader->initWithFilenames("shader.vert", "shader.frag");
myShader->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
myShader->link();
myShader->updateUniforms();
}
I dont see the triangle on screen. But I'm able to draw cocos2dx primitive objects (polygons , points , lines) by just swapping out whats in my drawTriangle function but not my vertices using openGL.
What am i missing here?
What must be changed to let me see the impression of flying around the whole fixed scene? My current code just lets me look from a fixed viewpoint at objects each one rotating around itself. Enabling glLoadIdentity() just stops their rotation. Note that 3dWidget::paintGL() is permanently called by a timer every 20ms.
void 3dWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0.5f, 0.5f, 0.5f);
glRotatef(3.0f, 1.0f, 1.0f, 1.0f);
glTranslatef(-0.5f, -0.5f, -0.5f);
glPushMatrix();
//glLoadIdentity();
for (int i = 0; i < m_cubes.count(); i++) {
m_cubes[i]->render();
}
glPopMatrix();
}
void Cube::render() {
glTranslatef(m_x, m_y, m_z); // local position of this object
glCallList(m_cubeId); // render code is in createRenderCode()
glTranslatef(-m_x, -m_y, -m_z);
}
void Cube::createRenderCode(int cubeId) {
m_cubeId = cubeId;
glVertexPointer(3, GL_FLOAT, 0, m_pCubePoints);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, m_pCubeColors);
glNewList(m_cubeId, GL_COMPILE);
{
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLE_STRIP, 0, m_numPoints);
glDisableClientState(GL_COLOR_ARRAY);
}
glEndList();
}
void 3dWidget::init(int w, int h)
{
...
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float aspect = w/(float)(h ? h : 1);
glFrustum(-aspect, aspect, -1, 1, 10, 100);
glTranslatef(0., 0., -12);
glMatrixMode(GL_MODELVIEW);
}
EDIT: It seems it's important to know that 2 cubes are created with the following 3D position coordinates (m_x, m_y, m_z):
void 3dWidget::createScene()
{
Cube* pCube = new Cube;
pCube->create(0.5 /*size*/, -0.5 /*m_x*/, -0.5 /*m_y*/, -0.5 /*m_z*/);
pCube = new Cube;
pCube->create(0.5 /*size*/, +0.5 /*m_x*/, +0.5 /*m_y*/, +0.5 /*m_z*/);
}
Use gluLookAt to position the camera. You apply it to the modelview matrix before any object transforms.
Obviously, you'll have to figure out a path for the camera to follow. That's up you and how you want the "flight" to proceed.
EDIT: Just to be clear, there's no camera concept, as such, in OpenGL. gluLookAt is just another transform that (when applied to the modelview matrix) has the effect of placing a camera at the prescribed location.
If you really are just trying to rotate the world, your code seems to perform the transforms in a reasonable order. I can't see why your objects rotate around themselves rather than as a group. It might help to present a SSCCE using glut.
Now I've found the reason by myself. It works as soon as I change method paintGL() to
void 3dWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#if 0 // not working
glTranslatef(0.5f, 0.5f, 0.5f);
glRotatef(3.0f, 1.0f, 1.0f, 1.0f);
glTranslatef(-0.5f, -0.5f, -0.5f);
#else // this works properly, they rotate horizontally around (0,0,0)
glRotatef(3.0f, 0.0f, 1.0f, 0.0f);
#endif
for (int i = 0; i < m_cubes.count(); i++) {
m_cubes[i]->render();
}
}
I don't get it exactly why, but it obviously appeared that some transformations had compensated in a way that the objects just rotate around itself. Thanks for your help anyway.
I think it's always better to let the scene rotate than to move by gluLookAt (beside the issue that finding the right formula for the angle of view is more difficult).