在OpenGL中绘制物体的轨迹

Posted

技术标签:

【中文标题】在OpenGL中绘制物体的轨迹【英文标题】:Draw trajectory of an object in OpenGL 【发布时间】:2017-04-02 04:08:52 【问题描述】:

当我点击屏幕上的任何位置时,我想制作绘制轨迹的代码。

我将初始中心点设置为起点。我想将单击鼠标按钮的另一点(目标点)设置为变量,但我不知道该怎么做。

float v1[3] =  -35.0f,  22.5f, 0.0f ;
float v2[3] =  -35.0f, -22.5f, 0.0f ;
float v3[3] =  0.0f,  42.5f, 0.0f ;
float v4[3] =  0.0f, -42.5f, 0.0f ;
float v5[3] =  35.0f,  22.5f, 0.0f ;
float v6[3] =  35.0f, -22.5f, 0.0f ;

这是对象的初始位置。 (6点星,2个三角形)

float px, py;
float center_s[3] =  0.0f, 0.0f, 0.0f ;
float center_d[3] =  px, py, 0.0f ;

center_s 是起点,center_d 是终点,变量pxpy

void lines(void) 
    glColor3f(1.0, 0.0, 0.0);

    glPointSize(5);
    glBegin(GL_POINTS);
    glVertex3fv(center_s);

    glLineWidth(1);
    glBegin(GL_LINES);
    glVertex3fv(center_s);
    glVertex3fv(center_d);

此函数用红线绘制从center_scenter_d 的轨迹。它还绘制了一个中心点。

case GLUT_LEFT_BUTTON:
    if (state == GLUT_DOWN) 
        x = mx;
        y = glutGet(GLUT_WINDOW_HEIGHT) - my;
        px = x + 35.0;
        py = y + 42.5;
        glutIdleFunc(lines);
    
    glutPostRedisplay();
    break;

这就是问题所在。当按下鼠标左键时,星星必须移动到单击的位置并计算位置的中心点并绘制线。但是如果我运行这些代码,则不会绘制运动轨迹。

请告诉我问题出在哪里。另外,星星必须以恒定的速度移动到点击的位置。 (不是传送)

以下是完整代码:

#include <stdlib.h>
#include <GL/glut.h>

float v1[3] =  -35.0f,  22.5f, 0.0f ;
float v2[3] =  -35.0f, -22.5f, 0.0f ;
float v3[3] =  0.0f,  42.5f, 0.0f ;
float v4[3] =  0.0f, -42.5f, 0.0f ;
float v5[3] =  35.0f,  22.5f, 0.0f ;
float v6[3] =  35.0f, -22.5f, 0.0f ;

float px, py;
float center_s[3] =  0.0f, 0.0f, 0.0f ;
float center_d[3] =  px, py, 0.0f ;

static GLfloat spin = 0.0;

float x = 400.0f, y = 442.5f;

float color1[3] =  1.0f, 1.0f, 1.0f ;
float color2[3] =  1.0f, 1.0f, 1.0f ;

int mode = 1;
int rotate = 1;

void init(void);
void triangle_1(void);
void triangle_2(void);
void lines(void);
void display(void);
void spinDisplay_1(void);
void spinDisplay_2(void);
void reshape(int, int);
void changeColor(int);
void mouse(int, int, int, int);

////////////////////////////////////////////////////////////////////

int main(int argc, char **argv) 
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(300, 300);
    glutCreateWindow("6-Point Star");

    init();

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMainLoop();

    return 0;


////////////////////////////////////////////////////////////////////

void init(void) 
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);


void triangle_1(void) 
    glColor3fv(color1);

    glBegin(GL_TRIANGLE_FAN);
    glVertex3fv(v1);
    glVertex3fv(v4);
    glVertex3fv(v5);

    glEnd();


void triangle_2(void) 
    glColor3fv(color2);

    glBegin(GL_TRIANGLE_FAN);
    glVertex3fv(v2);
    glVertex3fv(v3);
    glVertex3fv(v6);

    glEnd();


void lines(void) 
    glColor3f(1.0, 0.0, 0.0);

    glPointSize(5);
    glBegin(GL_POINTS);
    glVertex3fv(center_s);

    glLineWidth(1);
    glBegin(GL_LINES);
    glVertex3fv(center_s);
    glVertex3fv(center_d);


void display(void) 
    glClear(GL_COLOR_BUFFER_BIT);
    glPushMatrix();

    glTranslatef(x, y, 0.0f);
    glRotatef(spin, 0.0, 0.0, 1.0);

    triangle_1();
    triangle_2();

    glPopMatrix();

    glutSwapBuffers();


void spinDisplay_1(void) 
    spin = spin + 2.0;

    if (spin > 360.0) 
        spin = spin - 360.0;
    

    glutPostRedisplay();


void spinDisplay_2(void) 
    spin = spin - 2.0;

    if (spin < 360.0) 
        spin = spin + 360.0;
    

    glutPostRedisplay();


void reshape(int w, int h) 
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 500.0, 0.0, 500.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();


void changeColor(int n) 
    if (n == 1) 
        color1[0] = 0.0f, color1[1] = 0.0f, color1[2] = 1.0f;
        color2[0] = 0.0f, color2[1] = 1.0f, color2[2] = 0.0f;
    
    else if (n == 2) 
        color1[0] = 1.0f, color1[1] = 1.0f, color1[2] = 1.0f;
        color2[0] = 1.0f, color2[1] = 1.0f, color2[2] = 1.0f;
    


void mouse(int button, int state, int mx, int my) 
    switch (button) 
    case GLUT_LEFT_BUTTON:
        if (state == GLUT_DOWN) 
            x = mx;
            y = glutGet(GLUT_WINDOW_HEIGHT) - my;
            px = x + 35.0;
            py = y + 42.5;
            glutIdleFunc(lines);
        
        glutPostRedisplay();
        break;
    case GLUT_MIDDLE_BUTTON:
        if (state == GLUT_DOWN) 
            if (mode == 1) 
                changeColor(mode);
                mode = 2;
            
            else if (mode == 2) 
                changeColor(mode);
                mode = 1;
            
        
        glutPostRedisplay();
        break;
    case GLUT_RIGHT_BUTTON:
        if (state == GLUT_DOWN)
            if (rotate == 1) 
                glutIdleFunc(spinDisplay_1);
                rotate = 2;
            
            else if (rotate == 2) 
                glutIdleFunc(spinDisplay_2);
                rotate = 1;
            
        break;
    default:
        break;
    

【问题讨论】:

考虑到你最后三个问题,我建议你买一本关于 OpenGL 编程的好书。您添加到程序中的每一步似乎都有问题,这意味着最终您的整个应用程序是由一些 SO 成员编写的。 【参考方案1】:

您缺少一些glEnd(),除非您对center_scenter_d 有其他计划,否则您不需要它们。因为xycenter_s,而pxpycenter_d

所以首先在mouse() 中,只需将鼠标位置分配给pxpy 而不是xy

case GLUT_LEFT_BUTTON:
    if (state == GLUT_DOWN) 
        px = mx;
        py = glutGet(GLUT_WINDOW_HEIGHT) - my;
    

接下来,您需要一种方法来获取增量时间。增量时间是自上一帧以来经过的时间。 为方便起见,我在display() 的顶部添加了以下代码。

int timeNow = glutGet(GLUT_ELAPSED_TIME);
float delta = (float)(timeNow - timeLastFrame) / 1000.0f;
timeLastFrame = timeNow;

记得在你的全局变量中声明int timeLastFrame = 0;

现在(仍在display())我们可以计算行进路径的方向。我们通过计算两点之间的差异来做到这一点。然后计算长度并将差异归一化。

float dx = px - x;
float dy = py - y;

float length = sqrt(dx * dx + dy * dy);

dx /= length;
dy /= length;

现在你把它们放在一起。

if (length > 1.0f) 
    x += dx * speed * delta;
    y += dy * speed * delta;

所以当length &gt; 1.0 时,我们以float speed = 100.0f; 的速度向pxpy 移动(在你的全局变量中声明它)。

再次强调。是的,请从mouse() 中删除glutIdleFunc(lines)。然后我们将其添加到display() 中。 display() 的完整扩展现在应该如下所示:

void display(void) 
    int timeNow = glutGet(GLUT_ELAPSED_TIME);
    float delta = (float)(timeNow - timeLastFrame) / 1000.0f;
    timeLastFrame = timeNow;

    float dx = px - x;
    float dy = py - y;

    float length = sqrt(dx * dx + dy * dy);

    dx /= length;
    dy /= length;

    if (length > 1.0f) 
        x += dx * speed * delta;
        y += dy * speed * delta;
    

    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();
    glTranslatef(x, y, 0.0f);
    glRotatef(spin, 0.0, 0.0, 1.0);
    triangle_1();
    triangle_2();
    glPopMatrix();

    lines();

    glutSwapBuffers();

    glutPostRedisplay();

记住glutPostRedisplay()。如果不是,那么它不会重绘,因此不会动画。还记得#include &lt;math.h&gt;sqrt()

glClear() 之前的所有内容都可以移动到您的 glutIdleFunc() 回调中。

既然不需要center_scenter_d,那么lines() 可以归结为:

void lines(void) 
    glColor3f(1.0, 0.0, 0.0);

    glPointSize(5);
    glBegin(GL_POINTS);
    glVertex2f(px, py);
    glEnd();

    glLineWidth(1);
    glBegin(GL_LINES);
    glVertex2f(x, y);
    glVertex2f(px, py);
    glEnd();

结果应该是这样的:

供日后参考。如果你不想手工做线性代数。然后你可以使用GLM之类的东西。

【讨论】:

感谢您的回答。但是你能解释更多关于dx /= lengthx += dx * speed * delta的信息吗? 不客气! dxpxx 之间的区别。所以这可能是0 1000 或任何东西。我们需要标准化这种差异,使其介于-11 之间。基本上是以单位/法线向量的形式指向目标的方向。这意味着我们可以将此向量乘以x,然后新位置将恰好与该位置相距x。所以我们有我们的speed,我们将它乘以delta(自上一帧以来的时间),这样我们就知道要在帧上移动多少。 我想我必须学习线性代数......而且星星应该只在按下鼠标按钮时移动,但目前只要我运行程序它就会移动。所以我添加了一个全局变量trigger = 0;并将if条件length &gt; 1.0f编辑为length &gt; 1.0f &amp;&amp; trigger == 1。最后我添加了trigger = 1GLUT_DOWN 左键案例的声明。但是从星星到屏幕左下角仍然画了一条红线。我是否必须更改 display() 函数中的某些内容? 是的,在lines() 绘制线条的地方,还要添加if (trigger == 1),这样你就只能在trigger 设置为1 时绘制它。

以上是关于在OpenGL中绘制物体的轨迹的主要内容,如果未能解决你的问题,请参考以下文章

绘制大量三角形的有效方法(OpenGL)

来自物体的 Opengl 闪电

opengl 用粒子建模火箭火焰和蒸汽轨迹

opengl学习-利用模板测试勾画物体轮廓中出现的一个问题

OpenGL 五 - 案例解析- 物体的绘制 设置偏移颜色混合 + 观察者与物体的2种移动方式

用C++或者OpenGL或者Ogre实现鼠标移动轨迹