OpenGL 立方体平行斜投影的绘制

OpenGL 长方体平行斜投影的绘制

题目描述

绘制一个长方体的平行斜投影

分析

题目不长也很容易理解,首先搞明白什么是平行斜投影,这个问题最难的点在于使用OpenGL绘制三维图像,并且将投影和原图的效果都比较清晰和直观地展示出来。

平行斜投影

计算机图形学里面的投影主要分为两种,一种是平行投影,另一种是透视投影

平行投影就是平行光的投影,类似于太阳光照射人到地面形成影子这样的情形。即光源离被照射物体很远。

透视投影就是灯光照射杯子都桌面这样子的。即光源离被照射物体很近。

而平行投影又分为正投影和斜投影,这个顾名思义,很容易理解。

题目要求的就是平行投影中的后者了。

代码思路

思路一: 三维效果可以建立坐标系,然后把必要元素都画出来,只要有坐标系相信都是看的懂得。

思路二: 曾经看到过鼠标操作OpenGL这样的操作,可以实现拖拉改变图像视角,就很厉害,也很直观。

第一个画出来太丑了,直接第二个吧,不然交的作业老师不高兴给成绩。。。

必要函数介绍

这里介绍的是Glut开源库的介绍,引一个GLUT官方文档

http://freeglut.sourceforge.net/docs/api.php

鉴于这个库已经有20年没有更新了,欢迎大家试着用GLFW和GLEW做。给一个中文版的教程

https://learnopengl-cn.github.io/

最后再加一个OpenGL的官方网站。

https://www.opengl.org/

  • [ 如果有大佬搞定了win10下clion的glfw配置(运行时候没有 undefined reference 报错),请救救孩子谢谢!]

鼠标操作

关于鼠标操作,这边引一个博客教程来参考。

https://blog.csdn.net/m0_37876745/article/details/78147046

比较关键的函数在下表中罗列:

函数名作用
glutMouseFunc();处理鼠标点击
glutMotionFunc);处理鼠标拖动
glutPassiveMotionFunc()追踪鼠标位置

视图操作

主要关于三维坐标下的选取观测点以及设置观测角度问题。
还是给出两篇参考吧。

https://blog.csdn.net/peng6662001/article/details/7082436
https://blog.csdn.net/ivan_ljf/article/details/8764737

函数名作用
glMatrixMode()设置矩阵模式(投影/模型/纹理)
gluPerspective()设置透视效果参数
gluLookAt()设置观测点

编程实现

环境

  • win10
  • Clion
  • freeglut

代码

#include <GL/glut.h>
#include <math.h>
#define PI 3.1415926
static int init_angel=90, dx=-5, dy=-5;
static float r=50,h=90.0;
static float angel_trans_unit=float(PI/180.00);

void ChangeSize(int w, int h)
{
    // w, h是整个window的宽高
    // 这个函数是为了使窗口在被拉伸调整的时候实时重画图像
    glViewport(0,0,(GLsizei)w,(GLsizei)h);
    // 投影模式
    glMatrixMode(GL_PROJECTION);
    // 单位矩阵
    glLoadIdentity();
    // 产生透视效果
    gluPerspective(30.0,(float)w/h,1.0,1000.0);
    // 模版模式,用于绘图
    glMatrixMode(GL_MODELVIEW);
}

void Line(double x0, double y0, double z0, double x1, double y1, double z1){
    glLineWidth(3);
    glBegin(GL_LINES);
    glVertex3d(x0,y0,z0);
    glVertex3d(x1,y1,z1);
    glEnd();
    glFlush();
}

void Cube( double x[], double y[], double z[])
{
    // 绘制原长方体(黄色)
    glColor3f(1.0,1.0,0.0);
    // 用很蠢的的方法把长方体所有线画一遍
    Line(x[0],y[0],z[0],x[1],y[0],z[0]);
    Line(x[0],y[0],z[0],x[0],y[1],z[0]);
    Line(x[0],y[0],z[0],x[0],y[0],z[1]);
    Line(x[1],y[1],z[0],x[1],y[0],z[0]);
    Line(x[1],y[1],z[0],x[0],y[1],z[0]);
    Line(x[0],y[0],z[1],x[0],y[1],z[1]);
    Line(x[1],y[0],z[0],x[1],y[0],z[1]);
    Line(x[0],y[1],z[0],x[0],y[1],z[1]);
    Line(x[0],y[0],z[1],x[1],y[0],z[1]);
    Line(x[1],y[1],z[1],x[1],y[1],z[0]);
    Line(x[1],y[1],z[1],x[0],y[1],z[1]);
    Line(x[1],y[1],z[1],x[1],y[0],z[1]);
}

void  AXIS()
// 画个坐标轴
{
    // 红色的Z轴
    glColor3f(1.0,0.0,0.0);
    Line(0,0,-300,0,0,300);
    // 绿色的y轴
    glColor3f(0.0,1.0,0.0);
    Line(0,-300,0,0,300,0);
    // 蓝色的x轴
    glColor3f(0.0,0.0,1.0);
    Line(-300,0,0,300,0,0);
}

void Projection(double x[], double y[], double z[], double l, double alpha)
// 光线沿从(0,0,1)到(l*cos(alpha), l*sin(alpha), 0)方向
{
    double p[8][2];
    int count = 0;
    // 求出8个顶点的投影点
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++){
                p[count][0] = x[i] + z[k]*l*cos(alpha);
                p[count][1] = y[j] + z[k]*l*sin(alpha);
                count++;
            }
    // 投影的平面图是二维的
    // 把所有个投影点都交叉相连,最外围轮廓就是投影了
    //
    glColor3f(0.0,1.0,1.0);
    for(int i=0;i<8;i++)
        for(int j=i+1;j<8;j++){
            Line(p[i][0],p[i][1],0,p[j][0],p[j][1],0);
        }
    glFlush();
}

void RenderScene()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0f,1.0f,1.0f);
    glLoadIdentity();
    // gluLookAt()函数是指相机的位置
    // 一直看向原点,即保持原点是视野中心
    gluLookAt(r*cos(angel_trans_unit*init_angel), r*sin(angel_trans_unit*h), r*sin(angel_trans_unit*init_angel),
              0.0, 0.0, 0.0,
              0.0, 1, 0.0);
    // 设置长方体的一对对角点的坐标
    double x[] = {-1.0, -6.0}, y[] = {-1.0, -6.0}, z[] = {-1.0, -5.0};
    // 设置平行光的参数
    double l, alpha;
    l = 1;
    alpha = PI/4;
    AXIS();
    Cube(x, y, z);
    Projection(x, y, z, l, alpha);

    // 实现双缓冲的函数
    glutSwapBuffers();
}


void Mouse(int button, int state, int x, int y)
// 鼠标点击处理
{
    // 记录鼠标按下位置
    if(state == GLUT_DOWN)
        dx = x,dy = y;
}

void onMouseMove(int x,int y)
// 鼠标拖动处理
// (x,y):鼠标在屏幕平面的的新位置
{
    // 控制左右旋转
    init_angel += x - dx;
    // 控制上下旋转
    h += 1 * (y - dy);
    // 注意gluLookAt()的参数设置,h有些数值不能取
    if((int(h) % 180)==0) h++;
    // 保存此时的坐标
    dx = x,dy = y;
}

int main(int argc, char* argv[]) {
    // 初始化
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(1200, 700);
    glutCreateWindow("HomeWork2_1");
    // 可以体现图形在视野点看来的前后关系
    glEnable(GL_DEPTH_TEST);
    // 设置视图效果(透视投影、缩放)
    glutReshapeFunc(ChangeSize);
    // 两个设置,前者是为了画图,后者是为了拖动操作更流畅
    glutDisplayFunc(RenderScene);
    glutIdleFunc(RenderScene);
    // 鼠标动作的设定
    glutMouseFunc(Mouse);
    glutMotionFunc(onMouseMove);
    // 最后日常进入主循环
    glutMainLoop();
    return 0;
}

效果图

长方体及其平行斜投影
上图:红绿蓝是坐标轴,分别对应ZYX轴,平行光向量为( 2 2 \frac{\sqrt{2}}{2} 22 , 2 2 \frac{\sqrt{2}}{2} 22 ,-1),投影在z=0平面上。

思考与拓展

  1. 画图部分的代码应该可以在进一步,算法功底不够深,思考怎么优化
  2. 平行斜投影部分怎么才能做到更好的效果,这样子的乱遭遭的线不是个好主意
  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用OpenGL绘制立方体交投影,需要进行以下步骤: 1. 设置交投影矩阵 使用glOrtho函数设置交投影矩阵。该函数的原型如下: ``` void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal); ``` 其中,left、right、bottom和top表示投影平面的四个边界,nearVal和farVal表示近和远的裁剪平面距离,单位为坐标系中的单位。 2. 绘制立方体 使用glBegin和glEnd函数开始和结束绘制操作,并使用glVertex函数绘制立方体的顶点。注意,需要绘制立方体的六个面。 3. 设置模型视矩阵 使用glMatrixMode和glLoadIdentity函数将当前矩阵模式设置为模型视矩阵,并将其初始化为单位矩阵。 4. 设置立方体的位置和方向 使用glTranslatef、glRotatef和glScalef函数对立方体进行平移、旋转和缩放操作,以确定其在场景中的位置和方向。 完整的OpenGL代码如下所示: ``` #include <GL/glut.h> void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 设置交投影矩阵 glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1, 1, -1, 1, -1, 1); // 绘制立方体 glColor3f(1, 0, 0); glBegin(GL_QUADS); // front face glVertex3f(-0.5, -0.5, 0.5); glVertex3f( 0.5, -0.5, 0.5); glVertex3f( 0.5, 0.5, 0.5); glVertex3f(-0.5, 0.5, 0.5); // back face glVertex3f(-0.5, -0.5, -0.5); glVertex3f(-0.5, 0.5, -0.5); glVertex3f( 0.5, 0.5, -0.5); glVertex3f( 0.5, -0.5, -0.5); // top face glVertex3f(-0.5, 0.5, -0.5); glVertex3f(-0.5, 0.5, 0.5); glVertex3f( 0.5, 0.5, 0.5); glVertex3f( 0.5, 0.5, -0.5); // bottom face glVertex3f(-0.5, -0.5, -0.5); glVertex3f( 0.5, -0.5, -0.5); glVertex3f( 0.5, -0.5, 0.5); glVertex3f(-0.5, -0.5, 0.5); // left face glVertex3f(-0.5, -0.5, -0.5); glVertex3f(-0.5, -0.5, 0.5); glVertex3f(-0.5, 0.5, 0.5); glVertex3f(-0.5, 0.5, -0.5); // right face glVertex3f( 0.5, -0.5, 0.5); glVertex3f( 0.5, -0.5, -0.5); glVertex3f( 0.5, 0.5, -0.5); glVertex3f( 0.5, 0.5, 0.5); glEnd(); // 设置模型视矩阵 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // 设置立方体的位置和方向 glTranslatef(0, 0, -2); glRotatef(45, 1, 1, 0); glutSwapBuffers(); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(400, 400); glutCreateWindow("Orthographic Projection"); glutDisplayFunc(display); glEnable(GL_DEPTH_TEST); glutMainLoop(); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值