Related
I just did some math from Wikipedia for 3D projection because I noticed they were simple, library not needed. It does work but, the cube leaves a trail behind as it moves. Note that the cube doesn't actually move, I am actually changing the camera position which makes he cube look like it's moving.
There's no need to point out 100 bad practices that I am doing, I'm aware, this is just supposed to be be a quick test.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <glad/glad.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_opengl.h>
#include <math.h>
#include "utils.h"
#include "keys.h"
char p = 1;
typedef struct Vec3 {
float x;
float y;
float z;
} Vec3;
void Mat_iden(float *m, Uint32 s) {
Uint32 i = 0;
Uint32 unt = s + 1;
while (i < s) {
m[unt * i] = 1;
i++;
}
}
float one[3][3];
float two[3][3];
float three[3][3];
int main() {
SDL_Init(SDL_INIT_VIDEO);
IMG_Init(IMG_INIT_PNG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_Window *w = SDL_CreateWindow("Snapdoop", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 500, 500, SDL_WINDOW_OPENGL);
SDL_GLContext c = SDL_GL_CreateContext(w);
gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress);
Mat_iden(one[0], 3);
Mat_iden(two[0], 3);
Mat_iden(three[0], 3);
Shader s[2];
s[0] = Shade("/home/shambhav/eclipse-workspace/Snadoop/src/vs.glsl");
s[1] = Shade("/home/shambhav/eclipse-workspace/Snadoop/src/fs.glsl");
Shade_comp(&s[0], GL_VERTEX_SHADER);
Shade_comp(&s[1], GL_FRAGMENT_SHADER);
Program sp;
Prog_attach(&sp, s, 2);
printf("VS: %s\n", s[0].info);
printf("FS: %s\n", s[1].info);
printf("SP: %s\n", sp.info);
glDeleteShader(s[0].c);
glDeleteShader(s[1].c);
float v[48] = {
//Front
0.25, 0.25, 0.25, 1.0, 1.0, 0.0,
-0.25, 0.25, 0.25, 1.0, 0.0, 0.0,
-0.25, -0.25, 0.25, 0.0, 1.0, 1.0,
0.25, -0.25, 0.25, 0.0, 1.0, 0.0,
//Back
0.25, 0.25, -0.25, 0.0, 0.0, 1.0,
-0.25, 0.25, -0.25, 1.0, 0.0, 1.0,
-0.25, -0.25, -0.25, 1.0, 1.0, 1.0,
0.25, -0.25, -0.25, 0.0, 0.0, 0.0
};
unsigned int i[36] = {
//Front
0, 1, 2,
2, 3, 0,
//Right
0, 3, 7,
7, 4, 0,
//Left
1, 2, 6,
6, 5, 2,
//Back
4, 5, 6,
6, 7, 4,
//Up
0, 1, 5,
5, 4, 0,
//Down
3, 7, 2,
2, 6, 7
};
GLuint VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(i), i, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void *)(sizeof(float) * 3));
glEnableVertexAttribArray(1);
Vec3 cam = {1.0, 1.0, 1.0};
Vec3 theta = {0, 0, 0};
Key k = (const Key){ 0 };
printf("%d\n", k.alpha[9]);
SDL_Event e;
while (p) {
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
p = 0;
break;
case SDL_KEYDOWN:
*key(&k, e.key.keysym.sym) = 1;
break;
case SDL_KEYUP:
*key(&k, e.key.keysym.sym) = 0;
break;
}
}
if (*key(&k, SDLK_RIGHT)) {
cam.x += 0.01;
}
if (*key(&k, SDLK_LEFT)) {
cam.x -= 0.01;
}
if (*key(&k, SDLK_UP)) {
cam.y += 0.01;
}
if (*key(&k, SDLK_DOWN)) {
cam.y -= 0.01;
}
if (*key(&k, 'w')) {
theta.y += 0.01;
}
if (*key(&k, 's')) {
theta.y -= 0.01;
}
if (*key(&k, 'a')) {
theta.x -= 0.01;
}
if (*key(&k, 'd')) {
theta.x += 0.01;
}
if (*key(&k, 'z')) {
theta.z -= 0.01;
}
if (*key(&k, 'x')) {
theta.z += 0.01;
}
if (*key(&k, 'n')) {
cam.z += 0.01;
}
if (*key(&k, 'm')) {
cam.z -= 0.01;
}
one[1][1] = cos(theta.x);
one[1][2] = sin(theta.x);
one[2][1] = -sin(theta.x);
one[2][2] = cos(theta.x);
two[0][0] = cos(theta.y);
two[0][2] = -sin(theta.y);
two[2][0] = sin(theta.y);
two[2][2] = cos(theta.y);
three[0][0] = cos(theta.z);
three[0][1] = sin(theta.z);
three[1][0] = -sin(theta.z);
three[1][1] = cos(theta.z);
glUseProgram(sp.p);
glUniformMatrix3fv(2, 1, GL_FALSE, one[0]);
glUniformMatrix3fv(3, 1, GL_FALSE, two[0]);
glUniformMatrix3fv(4, 1, GL_FALSE, three[0]);
glUniform3f(5, cam.x, cam.y, cam.z);
glClear(GL_DEPTH_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
SDL_GL_SwapWindow(w);
}
glDeleteProgram(sp.p);
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
SDL_GL_DeleteContext(c);
SDL_DestroyWindow(w);
SDL_Quit();
return 0;
}
Vertex Shader(vs.glsl):
#version 450 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 tcol;
layout (location = 2) uniform mat3 x;
layout (location = 3) uniform mat3 y;
layout (location = 4) uniform mat3 z;
layout (location = 5) uniform vec3 c;
out vec3 col;
void main() {
vec3 d = x * y * z * (pos - c);
gl_Position.x = d.x / d.z;
gl_Position.y = d.y / d.z;
gl_Position.z = 0.0;
gl_Position.w = 1.0;
col = tcol;
}
Fragment Shader:
#version 450 core
out vec4 color;
in vec3 col;
void main() {
color = vec4(col, 1.0);
}
I think that keys.h and utils.h should not be here as they are not relevant to OpenGL. And this is a Minimum Reproducible Example as the only extra parts(keys.h and utils.h) are required for managing key data and loading shaders respectively.
Some keys in my code may be inverted, it's just bad code in all ways... Sorry for that.
This is an image I have taken after moving the cube(or the camera perspective to be accurate). One major thing to note is that it seems to be working perfectly other than the trail.
You need to clear the color buffer as well:
glClear(GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear clears the specified buffers. The buffers are specified with a bit mask. GL_COLOR_BUFFER_BIT indicates to clear the buffers currently enabled for color writing.
The short answer is that you need to change:
glClear(GL_DEPTH_BUFFER_BIT);
...to...
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
More details
Comments have asked for more elaboration, so I'll elaborate.
When you say glClear(GL_DEPTH_BUFFER_BIT) it clears out the pixel values in the Z-Buffer (depth buffer). When you say glClear(GL_COLOR_BUFFER_BIT) it clears out the RGBA channels of the pixels in the color buffer (sets them to the glClearColor). If you say glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) it clears both the Z-Buffer and the color buffer at the same time. That's what you want to do. You want to start each frame with a fresh black background and draw your content for that frame over top of it.
Think of it like setting each pixel to black and setting the depth value to zero. Actually, it will set the color buffer to the color specified by glClearColor and will set the depth value to the value specified by glClearDepth.
In your comment you said that you thought it "clears the functionality". That's not what glClear does. If you wanted to enable or disable writing to the depth buffer completely, you could do so with with glDepthMask. This function lets you completely disable writes to the depth buffer, potentially while still writing color values to the color buffer. There is a similar function called glColorMask that lets you select which channels of the color buffer (red, green, blue, and/or alpha) you want to write to as well. In this way you could potentially do interesting things like rendering only green, or even doing a special effect where you do a render pass in which you render only depth values and not color values (perhaps in preparation for knocking out a special effect to be applied in a subsequent pass.) glClear, conversely, actually sets the values in the pixels of the color buffer or depth buffer.
In the code you posted, you're only doing glClear(GL_DEPTH_BUFFER_BIT), which is only clearing the depth buffer, but not the color buffer. This essentially leaves all the paint on the canvas from last frame you drew, so leftover images from previous frames remain visible on the screen. You should be clearing both buffers.
Because you only draw your colorful square each frame, you draw a new square over top of whatever was in the buffer from last time. If you're double-buffering (common in full-screen graphics modes, but not windowed graphics modes), you may find that you're drawing over top of a frame from two-frames-ago, which may produce a strobing/flashing marquee effect.
The argument to glClear is called a bitmask. It uses each bit of the mask like a checkbox to select whether a particular kind of buffer should be cleared. Specifying GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT will logically OR the bits together creating a number with both bits set -- which is like checking both checkboxes, saying, "yes, please clear the depth buffer, and yes also clear the color buffer".
There can be up to four different kinds of buffers, not just color and depth. The four mask fields are GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_ACCUM_BUFFER_BIT, and GL_STENCIL_BUFFER_BIT. Each one of these is a bit-field value, a number with a single binary bit set, which can be logically OR'ed together like 4 individual checkboxes. In your application your render target may not have an accumulator buffer or a stencil buffer. Some render targets don't even make use of a depth buffer. It's totally up to how you created your render buffer originally. In your case it looks like you have a buffer with color and depth. So when it comes time to clear the buffer, in preparation for rendering the frame, you'll want to make sure you check both boxes, effectively asking for both the color and depth components of your buffers to be cleared. Do so by passing GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT as the argument to glClear.
The use of bit-fields here is so exemplary, that glClear is actually used on the Wikipedia page for Mask_(computing) - Uses of bitmasks to explain how bitmasks can be used!
I have such a OpenGL-4 code (see below). I have created a buffer for vertices and wanted to initialize it with the help of for-loop in init().
It should be a circle of 30 lines (surrounded by circle later) but I can see only the first line on the screen. And I have done such things before with glVertex. But with VOA I don't really know what to do; I tried a lot but I'm really puzzled; May be It's some stupid mistake or my misunderstanding, I failed to find it. is it possible to do so with VOAs at all?
GLuint lineArrayID;
GLuint lineVertexBuffer;
GLuint numLines = 30;
static GLfloat lineVertexBufferData[30][2] = {};
void init() {
draw_circle();
glClearColor(1.0, 1.0, 1.0, 1.0);//background of the window
GLfloat x, y;
double angle;
GLfloat radius = 5.0;
angle = 2 * PI / 30;
int j = 0;
float i = 0;
for (i = -PI; i < -PI; i += 0.15){
std::cout << " +"<<std:: endl;
x = sin(i);
y = cos(i);
lineVertexBufferData[j][0] = 0.0; lineVertexBufferData[j][1] = 0.0;
lineVertexBufferData[j][0] = x; lineVertexBufferData[j][1] = y;
j++;
}
// compile and activate the desired shader program
shaderProgramID = loadShaders("D.vs", "D.fs");
glUseProgram(shaderProgramID);
// generate Buffers for our objects
prepareLines();
}
void prepareLines(){
glGenVertexArrays(1, &lineArrayID); //gen one array object
glBindVertexArray(lineArrayID); //binds it
glGenBuffers(1, &lineVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, lineVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, numLines * 60 * sizeof(GLfloat), lineVertexBufferData, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
static void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
// drawing the lines
glBindVertexArray(lineArrayID);
glCallList(1);
glDrawArrays(GL_LINES, 0, numLines * 2);
glBindVertexArray(0);
transform();
//glRotatef(grad, 0, 0, 1);
//glutSwapBuffers();
glFlush();
}
numLines * 60 * sizeof(GLfloat)
That is way too big, and doesn't match the size of linearVertexBufferData at all. It should be numLines * 2 * sizeof(GLfloat) or even just sizeof(lineVertexBufferData)
glCallList(1);
This is also invalid; you never create any display lists, so it's unlikely that display list one exists. If you're using VAO's, you shouldn't need to create them anyway.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
The second parameter means that each vertex has three components, but judging from lineVertexBufferData, it should only have two.
glDrawArrays(GL_LINES, 0, numLines * 2);
The third parameter is the number of vertices to render, not number of components. This should not be multiplied by two.
//glutSwapBuffers();
glFlush();
SwapBuffers is correct here, not glFlush (which is almost never needed).
You're also calling transform() after you draw, when it should probably be before, otherwise your transformations will be delayed a frame.
I have a small code snippet which loads an image from a PNG file, then modifies the image data in memory by making a specific color transparent (setting alpha to 0 for that color). Here's the code itself:
static gboolean expose (GtkWidget *widget, GdkEventExpose *event, gpointer userdata)
{
int width, height, stride, x, y;
cairo_t *cr = gdk_cairo_create(widget->window);
cairo_surface_t* image;
char* ptr;
if (supports_alpha)
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */
else
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* opaque white */
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
image = cairo_image_surface_create_from_png ("bg.png");
width = cairo_image_surface_get_width (image);
height = cairo_image_surface_get_height (image);
stride = cairo_image_surface_get_stride (image);
cairo_surface_flush (image);
ptr = (unsigned char*)malloc (stride * height);
memcpy (ptr, cairo_image_surface_get_data (image), stride * height);
cairo_surface_destroy (image);
image = cairo_image_surface_create_for_data (ptr, CAIRO_FORMAT_ARGB32, width, height, stride);
cairo_surface_flush (image);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
char alpha = 0;
unsigned int z = *((unsigned int*)&ptr [y * stride + x * 4]);
if ((z & 0xffffff) == 0xffffff) {
z = (z & ~0xff000000) | (alpha & 0xff000000);
*((unsigned int*) &ptr [y * stride + x * 4]) = z;
}
}
}
cairo_surface_mark_dirty (image);
cairo_surface_write_to_png (image, "image.png");
gtk_widget_set_size_request (GTK_OBJECT (window), width, height);
gtk_window_set_resizable (GTK_OBJECT (window), FALSE);
cairo_set_source_surface (cr, image, 0, 0);
cairo_paint_with_alpha (cr, 0.9);
cairo_destroy (cr);
cairo_surface_destroy (image);
free (ptr);
return FALSE;
}
When I dump the modified data to PNG, transparency is actually there. But when the same data is used as a source surface for painting, there's no transparency. What might be wrong?
Attachments:
image.png - modified data dumped to file for debugging purposes,
demo.png - actual result
bg.png - source image, is omitted due to stackoverflow restrictions, it's simply black rounded rectangle on the white background. Expected result is black translucent rectangle and completely transparent fields, not white, like these on the demo.png.
Setting alpha to 0 means that the color is completely transparent. Since cairo uses pre-multiplied alpha, you have to set the pixel to 0, since otherwise the color components could have higher values than the alpha channels. I think cairo chokes on those super-luminscent pixels.
So instead of this code:
if ((z & 0xffffff) == 0xffffff) {
z = (z & ~0xff000000) | (alpha & 0xff000000);
*((unsigned int*) &ptr [y * stride + x * 4]) = z;
}
You should try the following:
if ((z & 0xffffff) == 0xffffff) {
*((unsigned int*) &ptr [y * stride + x * 4]) = 0;
}
And while we are at it:
Doesn't (z & 0xffffff) == 0xffffff check if the green, blue and alpha channels are all at 100% and ignores the red channel? Are you sure that's really what you want? z == 0xffffffff would be opaque white.
Instead of using unsigned int, it would be better if you used uint32_t for accessing the pixel data. Portability!
Your code assumes that cairo_image_surface_create_from_png() always gives you an image surface with format ARGB32. I don't think that's necessarily always correct and e.g. RGB24 is possible as well.
I think I would do something like this:
for (y = 0; y < height; y++) {
uint32_t row = (uint32_t *) &ptr[y * stride];
for (x = 0; x < width; x++) {
uint32_t px = row[x];
if (is_expected_color(px))
row[x] = 0;
}
}
I am trying to put lights, materials and shadows to my robot arm but unfortunately something weird happens (please compile or see the below picture), now I am still annoying by
1) Not showing correct lighting and reflection properties as well as material properties
2) No shadow painted, although I have done the shadow casting in function "void showobj(void)"
I appreciate if anyone can help, I have already working for it 2 days with no progress :(
The following is my code
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <GL/glut.h>
#include "gsrc.h"
#include <Windows.h>
const double PI = 3.14159265;
// angles to rotate the base, lower and upper arms of the robot arm
static GLfloat theta, phi, psi = 0.0;
//Starting time
double startT;
//Time Diff variable
double dif,startTime,endTime,deltaT;
//define n
double n = 3;
//Set the parameters of the light number one
GLfloat Xs = 35.0;
GLfloat Ys = 35.0;
GLfloat Zs = 35.0;
//Shadow color
GLfloat shadowcolor[] = {0.0,0.0,0.0};
//initialize the window and everything to prepare for display
void init_gl() {
//set display color to white
glClearColor(1,1,1,0);
//clear and enable z-buffer
glClear (GL_DEPTH_BUFFER_BIT);
glEnable (GL_DEPTH_TEST);
//clear display window
glClear(GL_COLOR_BUFFER_BIT);
}
//Draw the base of the robot arm
void draw_base(){
glPushMatrix();
//to create the quadric objects
GLUquadric *qobj,*qobjl,*qobju;
qobj = gluNewQuadric();
qobjl = gluNewQuadric();
qobju = gluNewQuadric();
//set the color of the cylinder
glColor3f(1.0,0.0,0.0);
//Re-position the cylinder (x-z plane is the base)
glRotatef(-90,1.0,0.0,0.0);
//Draw the cylinder
gluCylinder(qobj, 30.0, 30.0, 40.0, 40.0, 40.0);
//Draw the upper disk of the base
gluDisk(qobju,0,30,40,40);
glPushMatrix();
//Change the M(lowdisk<updisk)
glTranslatef(0,0,40);
glColor3f(0,0,0);
//Draw the lower disk of the base
gluDisk(qobjl,0,30,40,40);
glPopMatrix();
glPopMatrix();
}
/***********************Texture Work Starts************************************/
//Load the raw file for texture
/* Global Declarations */
#define IW 256 // Image Width
#define IH 256 // Image Height
//3D array to store image data
unsigned char InputImage [IW][IH][4];
// Read an input image from a .raw file with double
void ReadRawImage ( unsigned char Image[][IH][4] )
{
FILE *fp;
int i, j, k;
char* filename;
unsigned char temp;
filename = "floor.raw";
if ((fp = fopen (filename, "rb")) == NULL)
{
printf("Error (ReadImage) : Cannot read the file!!\n");
exit(1);
}
for ( i=0; i<IW; i++)
{
for ( j=0; j<IH; j++)
{
for (k = 0; k < 3; k++) // k = 0 is Red k = 1 is Green K = 2 is Blue
{
fscanf(fp, "%c", &temp);
Image[i][j][k] = (unsigned char) temp;
}
Image[i][j][3] = (unsigned char) 0; // alpha = 0.0
}
}
fclose(fp);
}
/****************************Texture Work Ends***************************************/
/****************************Light and Shadows***************************************/
void lightsrc(){
GLfloat light1PosType [] = {Xs, Ys, Zs, 1.0};
//GLfloat light2PosType [] = {0.0, 100.0, 0.0, 0.0};
glLightfv(GL_LIGHT1, GL_POSITION, light1PosType);
//glEnable(GL_LIGHT1);
//glLightfv(GL_LIGHT2, GL_POSITION, light2PosType);
//glEnable(GL_LIGHT2);
GLfloat whiteColor[] = {1.0, 1.0, 1.0, 1.0};
GLfloat blackColor[] = {0.0, 0.0, 0.0, 1.0};
glLightfv(GL_LIGHT1, GL_AMBIENT, blackColor);
glLightfv(GL_LIGHT1, GL_DIFFUSE, whiteColor);
glLightfv(GL_LIGHT1, GL_SPECULAR, whiteColor);
glEnable(GL_LIGHT1);
glEnable( GL_LIGHTING );
}
/****************************Light and Shadows work ends***************************************/
//Draw the 2x2x2 cube with center (0,1,0)
void cube(){
glPushMatrix();
glTranslatef(0,1,0);
glutSolidCube(2);
glPopMatrix();
}
//Draw the lower arm
void draw_lower_arm(){
glPushMatrix();
glScalef(15.0/2.0,70.0/2.0,15.0/2.0);//scale half is enough (some part is in the negative side)
cube();
glPopMatrix();
}
//Draw the upper arm
void draw_upper_arm(){
glPushMatrix();
glScalef(15.0/2.0,40.0/2.0,15.0/2.0);//scale half is enough (some part is in the negative side)
cube();
glPopMatrix();
}
void drawCoordinates(){
glBegin (GL_LINES);
glColor3f (1,0,0);
glVertex3f (0,0,0);
glVertex3f (600,0,0);
glColor3f (0,1,0);
glVertex3f (0,0,0);
glVertex3f (0,600,0);
glColor3f (0,0,1);
glVertex3f (0,0,0);
glVertex3f (0,0,600);
glEnd();
}
//To draw the whole robot arm
void drawRobot(){
//Robot Drawing Starts
//Rotate the base by theta degrees
glRotatef(theta,0.0,1.0,0.0);
//Draw the base
draw_base();
//M(B<La)
glTranslatef(0.0,40.0,0.0);
//Rotate the lower arm by phi degree
glRotatef(phi,0.0,0.0,1.0);
//change the color of the lower arm
glColor3f(0.0,0.0,1.0);
//Draw the lower arm
draw_lower_arm();
//M(La<Ua)
glTranslatef(0.0,70.0,0.0);
//Rotate the upper arm by psi degree
glRotatef(psi,0.0,0.0,1.0);
//change the color of the upper arm
glColor3f(0.0,1.0,0.0);
//Draw the upper arm
draw_upper_arm();
//Drawing Finish
glutSwapBuffers();
}
void showobj(void) {
//set the projection and perspective parameters/arguments
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 1000 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(-200, 300, 200, 0, 0, 0, 0,1,0 );
// get the rotation matrix from the rotation user-interface
glMultMatrixf(gsrc_getmo() );
//Clear the display and ready to show the robot arm
init_gl();
//put the light source
lightsrc();
//Draw coordinates
drawCoordinates();
//give material properties
GLfloat diffuseCoeff[] = {0.2, 0.4, 0.9, 1.0}; // kdR= 0.2, kdG= 0.4, kdB= 0.9
GLfloat specularCoeff[] = {1.0, 1.0, 1.0, 1.0}; //
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuseCoeff);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularCoeff);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0 ); // ns= 25
//Draw the ground floor
glColor3f(0.4,0.4,0.4);
glPushMatrix();
glRotatef(90,1,0,0);
glRectf(-500,-500,500,500);
glPopMatrix();
int i,j;
GLfloat M[4][4];
for (i=0; i<4; i++){
for (j=0; j<4; j++){
M[i][j] = 0;
}
M[0][0]=M[1][1]=M[2][2]=1;
M[2][3]=-1.0/Zs;
}
//Start drawing shadow
drawRobot(); // draw the objects
glPushMatrix( ); // save state
glMatrixMode(GL_MODELVIEW);
glTranslatef(Xs, Ys, Zs);// Mwc←s
glMultMatrixf(M[4]);// perspective project
glTranslatef(-Xs, -Ys, -Zs);// Ms←wc
glColor3fv (shadowcolor);
//Draw the robot arm
drawRobot();
glPopMatrix(); // restore state
//Shadow drawing ends
glFlush ();
}
//To animate the robot arm
void animate(void)
{
//get the end time
endTime = timeGetTime();
//float angle;
//calculate deltaT
deltaT = (endTime - startTime); //in msecs
//float test;
float deltaTSecs = deltaT/1000.0f; //in secs
//apply moving equation
psi = (90.0) * 0.50 * (1-cos((deltaTSecs/(n+1)) * PI));
glutPostRedisplay ();
}
void main (int argc, char** argv)
{
glutInit(&argc, argv);
//DOUBLE mode better for animation
// Set display mode.
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB);
glutInitWindowPosition( 50, 100 ); // Set top-left display-window position.
glutInitWindowSize( 400, 300 ); // Set display-window width and height.
glutCreateWindow( "Robot arm : my first self-learning opengl program" ); // Create display window.
// Register mouse-click and mouse-move glut callback functions
// for the rotation user-interface.
//Allow user to drag the mouse and view the object
glutMouseFunc( gsrc_mousebutton );
glutMotionFunc( gsrc_mousemove );
//record the starting time
startTime = timeGetTime();
// Display everything in showobj function
glutDisplayFunc(showobj);
//Perform background processing tasks or continuous animation
glutIdleFunc(animate);
glutMainLoop();
}
your screen flashes because you are calling glutSwapBuffers() in drawRobot(). That makes your screen repaint two times, once when you draw the robot, and once more when you draw the shadow. Also, you are missing glPushMatrix() at the beginning of drawRobot() and glPopMatrix() at the end. You need to put it there, otherwise it will affect rendering afterwards (the shadow will move with the upper link of the arm).
Then, you specify the shadow matrix wrong. Let's try this:
int i,j;
GLfloat M[4][4];
for (i=0; i<4; i++){
for (j=0; j<4; j++){
M[i][j] = 0;
}
}
M[0][0]=M[1][1]=M[2][2]=1;
M[2][3]=-1.0/Zs;
drawRobot(); // draw the objects
//Start drawing shadow
glEnable(GL_CULL_FACE);
glDisable(GL_LIGHTING); // want constant-color shadow
glPushMatrix( ); // save state
glMatrixMode(GL_MODELVIEW);
glTranslatef(Xs, Ys, Zs);// Mwc←s
glMultMatrixf(&M[0][0]);// perspective project
glTranslatef(-Xs, -Ys, -Zs);// Ms←wc
glColor3fv (shadowcolor);
//Draw the robot arm
drawRobot();
glPopMatrix(); // restore state
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHTING); // enable again ...
//Shadow drawing ends
Also, you can see i've added GL_CULL_FACE arround the shadow, it is to avoid depth fighting. This more or less fixes it technically.
But still - the shadow position is calculated incorrectly. Let's try looking at projection shadows.
So first, we need to have position for the ground plane and for the light:
float g[] = {0, 1, 0, 0}; // ground plane
float l[] = {20, 300, 50, 1}; // light position and "1"
That is a plane equation and a homogenous light position (normal 3D position, padded with a "1"). Then you throw away your shadow matrix setup (glTranslatef(), glMultMatrixf() and glTranslatef()) and call myShadowMatrix(g, l) instead, so it becomes:
glPushMatrix( ); // save state
glMatrixMode(GL_MODELVIEW);
float g[] = {0, 1, 0, 0}; // ground plane
float l[] = {20, 300, 50, 1}; // light position and "1"
myShadowMatrix(g, l);
glColor3fv (shadowcolor);
//Draw the robot arm
drawRobot();
glPopMatrix(); // restore state
And that mostly does work. There is still a lot of z-fighting going on, and the shadow has four different colors. As for the colors, stop calling glColor3f() in drawRobot(), as for the z-fighting, use this:
glPolygonOffset(-1, -1);
glEnable(GL_POLYGON_OFFSET_FILL);
// before
// draw shadow
glDisable(GL_POLYGON_OFFSET_FILL);
// afterwards
And that makes one nice planar shadows demo :). Cheers ...
sw.
I'm trying to write a Cairo program to black-fill the entire image and then draw another rectangle inside of it a different color. Eventually, I'm going to make this a program that generates a .png of the current time that looks like a digital clock. For now, this is where I'm getting hung up.
Here's my code:
#include <stdio.h>
#include <cairo.h>
//on color: 0.6, 1.0, 0
//off color: 0.2, 0.4, 0
int prop_number_width;
int prop_number_height;
int prop_width;
int prop_height;
int prop_space_width;
int prop_space_height;
double width;
double height;
double w_unit;
double h_unit;
void draw_number(cairo_t* cr, int unit_width, int num);
int main(int argc, char** argv) {
/* proportional sizes:
* the widths and heights of the diagram
*/
prop_number_width = 5; //how many spaces the number takes up
prop_number_height = 6;
prop_space_width = 1; //extra width on each side
prop_space_height = 1; //extra height on each side
prop_width = 25 + (2 * prop_space_width); //width of diagram
prop_height = 6 + (2 * prop_space_height); //height of diagram
/* actual sizes:
* the pixel value of different sizes
*/
width = 200.0;
height = 100.0;
w_unit = width / prop_width;
h_unit = height / prop_height;
//begin cairo stuff
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int)width, (int)height);
cairo_t* cr = cairo_create(surface);
//black fill
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_rectangle(cr, 0.0, 0.0, width, height); //cr ref, x, y, width, height
cairo_fill_preserve(cr);
//draw numbers from left to right
draw_number(cr, 0, 1);
//draw_number(cr, 6, 3);
//draw_number(cr, 14, 3);
//draw_number(cr, 20, 7);
//draw in colons
cairo_destroy(cr);
cairo_surface_write_to_png(surface, "test.png");
cairo_surface_destroy(surface);
return 0;
}
void draw_number(cairo_t* cr, int unit_width, int num) {
//determine the box size that the number will be drawn in
double box_x = w_unit * (prop_space_width + unit_width);
double box_y = h_unit * prop_space_height;
double box_width = w_unit * prop_number_width;
double box_height = h_unit * prop_number_height;
printf("{box_x: %lf box_y: %lf} {box_width: %lf box_height: %lf}\n", box_x, box_y, box_width, box_height);
cairo_set_source_rgb(cr, 0.2, 0.4, 0);
cairo_rectangle(cr, box_x, box_y, box_width, box_height);
cairo_fill_preserve(cr);
}
The problem is with this code it draws the rectangle to take up the whole image where from the printf's it should only take up a small part. Does anybody know how I can make this rectangle show up as the correct size?
I should have looked at the API more carefully. I needed to do cairo_fill() instead of cairo_fill_preserve(). Apparently, the first call to cairo_fill_preserve() was keeping the original rectangle and always filling that one.