编程题目:利用Opengl绘制一个三角形。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编程题目:利用Opengl绘制一个三角形。相关的知识,希望对你有一定的参考价值。

编程题目:利用Opengl绘制一个三角形。请给出详细的编程内容!答的好追加!

//编译环境VS2005 选WIN32应用程序, 我从来不用MFC

#include <GL/GL.h>
#include <gl/GLU.h>
#include <gl/GLAux.h>
#include <math.h>

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")

#define MAX_LOADSTRING 100

//定义递归调用阀值,即其中最小三角形边长
#define SIDELENGHT (0.05)

// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name

//自定义全局变量
HGLRC g_hRC = NULL; //OpenGL 绘图上下文
HDC g_hDC = NULL; //设备上下文
HWND g_hWnd = NULL; //保存当前窗口句柄

//定义三角形顶点指针
typedef struct tagTRIANGLEPOINT

GLfloat tpPointX;
GLfloat tpPointY;
TRIANGLEPOINT,*LTRIANGLEPOINT;

//定义最外面大三角形三个点
TRIANGLEPOINT g_TrianglePoint[3] = 0.0, 0.0, 5.0, 0.0, 2.5,2.5;

// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

//自定义函数
void EnableOpenGL(HWND hWnd);
void SceneInit();
void SceneResizeViewport();
void DisableOpenGL();
void Display();

void DrawTriangle(LTRIANGLEPOINT pTrianglePoint, int iPointNum = 3);
void DrawLargeTriangle(LTRIANGLEPOINT pTrianglePoint, int iPointNum);

int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)

UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;

// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_OPENGLTRIANGLE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))

return FALSE;


EnableOpenGL(g_hWnd);
SceneInit();
SceneResizeViewport();

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_OPENGLTRIANGLE));

// Main message loop:
ZeroMemory(&msg, sizeof(msg));

while ( msg.message != WM_QUIT )

if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))

if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

TranslateMessage(&msg);
DispatchMessage(&msg);


else

Display();
SwapBuffers(g_hDC);



DisableOpenGL();

return (int) msg.wParam;


//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage are only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_OPENGLTRIANGLE));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_OPENGLTRIANGLE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);


//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)

return FALSE;


g_hWnd = hWnd;

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;


//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message)

case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)

case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);

break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);

return 0;


// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

UNREFERENCED_PARAMETER(lParam);
switch (message)

case WM_INITDIALOG:
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)

EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;

break;

return (INT_PTR)FALSE;


//初始化opengl
void EnableOpenGL(HWND hWnd)

PIXELFORMATDESCRIPTOR pfd;
int iFormat;
g_hDC = GetDC(hWnd);

ZeroMemory(&pfd, sizeof(pfd));

pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW| PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;

iFormat = ChoosePixelFormat( g_hDC, &pfd );
SetPixelFormat( g_hDC, iFormat, &pfd );

g_hRC = wglCreateContext(g_hDC);
wglMakeCurrent( g_hDC, g_hRC);


//设置着色模式
void SceneInit()

glShadeModel(GL_SMOOTH);

glClearColor(1.0, 1.0, 1.0, 0.5);

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);



//设置视口
void SceneResizeViewport()

RECT rect;
int w,h;
GLfloat aspect;
GetClientRect( g_hWnd, &rect );
w = rect.right - rect.left;
h =rect.bottom - rect.top;
aspect = (GLfloat)w/ (GLfloat)h;

glViewport(0,0,w,h);//设置视口
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 5.0, 0.0, 5.0/aspect);


void DisableOpenGL()
wglMakeCurrent(NULL, NULL);
wglDeleteContext(g_hRC);
ReleaseDC(g_hWnd, g_hDC);


void Display()

glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

DrawLargeTriangle(g_TrianglePoint, 3);
DrawTriangle(g_TrianglePoint, 3);
glFlush();


//画大三角形
void DrawLargeTriangle(LTRIANGLEPOINT pTrianglePoint, int iPointNum)

glBegin(GL_LINE_LOOP);

for ( int i = 0; i < 3; ++i)

glColor3f(1.0,0.0,0.0);
glVertex2f(pTrianglePoint[i].tpPointX,pTrianglePoint[i].tpPointY);


glEnd();


//递归函数
void DrawTriangle(LTRIANGLEPOINT pTrianglePoint, int iPointNum)


//得到中间三角形的三个点
TRIANGLEPOINT trianglepoint[3];
for (int i = 0; i < 3; ++i)

if ( i == 2)

trianglepoint[i].tpPointX = (pTrianglePoint[i].tpPointX + pTrianglePoint[0].tpPointX) / 2;
trianglepoint[i].tpPointY = (pTrianglePoint[i].tpPointY + pTrianglePoint[0].tpPointY) / 2;

else

trianglepoint[i].tpPointX = (pTrianglePoint[i].tpPointX + pTrianglePoint[i+1].tpPointX) / 2;
trianglepoint[i].tpPointY = (pTrianglePoint[i].tpPointY + pTrianglePoint[i+1].tpPointY) / 2;



glBegin(GL_LINE_LOOP);

for ( int i = 0; i < 3; ++i)

glColor3f(1.0,0.0,0.0);
glVertex2f(trianglepoint[i].tpPointX,trianglepoint[i].tpPointY);


glEnd();

//构建其它三角
TRIANGLEPOINT trianglepoint1[3];
TRIANGLEPOINT trianglepoint2[3];
TRIANGLEPOINT trianglepoint3[3];

ZeroMemory(&trianglepoint1, sizeof(trianglepoint1));
ZeroMemory(&trianglepoint2, sizeof(trianglepoint2));
ZeroMemory(&trianglepoint2, sizeof(trianglepoint2));

trianglepoint1[0] = pTrianglePoint[0];
trianglepoint1[1] = trianglepoint[0];
trianglepoint1[2] = trianglepoint[2];

trianglepoint2[0] = pTrianglePoint[1];
trianglepoint2[1] = trianglepoint[1];
trianglepoint2[2] = trianglepoint[0];

trianglepoint3[0] = pTrianglePoint[2];
trianglepoint3[1] = trianglepoint[2];
trianglepoint3[2] = trianglepoint[1];

//设定边界
if(pow(abs(trianglepoint1[0].tpPointX - trianglepoint1[1].tpPointX), 2)
+pow(abs(trianglepoint1[0].tpPointY - trianglepoint1[1].tpPointY), 2) > SIDELENGHT)
DrawTriangle(trianglepoint1, iPointNum);

if(pow(trianglepoint2[0].tpPointX - trianglepoint2[1].tpPointX, 2)
+pow(trianglepoint2[0].tpPointY - trianglepoint2[1].tpPointY, 2) > SIDELENGHT)
DrawTriangle(trianglepoint2, iPointNum);

if(pow(trianglepoint2[0].tpPointX - trianglepoint2[1].tpPointX, 2)
+pow(trianglepoint2[0].tpPointY - trianglepoint2[1].tpPointY, 2) > SIDELENGHT)
DrawTriangle(trianglepoint3, iPointNum);
参考技术A void CTes1View::DrawScene()

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕及深度缓存 SwapBuffers(wglGetCurrentDC());
glLoadIdentity(); // 重置
glTranslatef(-1.5f,0.0f,-6.0f); // 左移 1.5 单位,并移入屏幕 6.0

glBegin(GL_TRIANGLES); // 绘制三角形

glVertex3f(0.0f,0.0f,0.0f); // 上顶点

glVertex3f(0.0f, 0.0f,-1.0f); // 左下顶点

glVertex3f(0.0f,1.0f,0.0f); // 右下顶点
glEnd(); // 三角形绘制结束

glFinish();
参考技术B glBegin(GL_TRIANGLES);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(0.0f, 0.0f,-1.0f);
glVertex3f(0.0f,1.0f,0.0f);
glEnd();

OpenGL学习脚印: 绘制一个三角形

写在前面
接着上一节内容,开发环境搭建好后,我们当然想立即编写3D应用程序了。不过我们还需要些耐心,因为OpenGL是一套底层的API,因而我们要掌握的基本知识稍微多一点,在开始绘制3D图形之前,本节我们将通过绘制一个三角形的程序来熟悉现代OpenGL的概念和流程。

通过本节可以了解到:

  • 缓存对象VAO和VBO
  • GLSL着色器程序的编译、链接和使用方法
  • OpenGL绘图的基本流程

绘图流水线简要了解

与使用高级绘图API(例如java里swing绘图,MFC里的绘图)不同,使用OpenGL绘制图形时需要对底层知识有所了解。在现代OpenGL中,完成图形绘制的流水线与旧版的固定流水线有所不同,现代OpenGL程序中允许用户自己定制着色器,这使得绘图更灵活。现代绘图流水线如下图所示(来自:opengl wiki Rendering_Pipeline_Overview):
这里写图片描述
这个绘图流水线是比较复杂的,初学时只需要关注vertex shader顶点着色器和Fragment shader片元着色器即可。顶点着色器负责将用户指定的顶点转换为内部表示,片元着色器决定最终生成图像的颜色。顶点着色器的和片元着色器之间可以通过传递变量来沟通。使用这两个着色器就可以绘制基本的图形了,主要的流程是:
(1) 用户在程序中指定或者加载顶点属性数据
(2) 将顶点属性数据传送到GPU,由顶点着色器处理顶点数据
(3) 由片元着色器负责最终图形的颜色
根据这个步骤,下面逐一熟悉相关概念和操作。

VBO和VAO

在OpenGL程序中指定或者加载的数据是存储在CPU中的,要加快图形渲染,必定要充分利用GPU的优势,因此需要将数据发送到GPU中。在GPU中,VBO即vertex buffer object,顶点缓存对象负责实际数据的存储;而VAO即 vertex array object, 记录数据的存储和如何使用的细节信息。

OpenGL是一个状态机(state machine),我们绘制图形时需要在不同的状态之间切换。例如上一节中通过glClearColor设置清除颜色缓冲区时设定的颜色,OpenGL则记住了这一状态,当调用glClear时则使用这个颜色重置颜色缓冲区。直到再次使用glClearColor设置不同颜色为止,OpenGL会一直使用这个状态值。

使用VAO的优势就在于,如果有多个物体需要绘制,那么我们设置一次绘制物体需要的顶点数据、数据解析方式等信息,然后通过VAO保存起来后,后续的绘制操作不再需要重复这一过程,只需要将VAO设定为当前VAO,那么OpenGL则会使用这些状态信息。当场景中物体较多时,优势十分明显。VAO和VBO的关系如下图所示(图片来自Best Practices for Working with Vertex Data):
这里写图片描述
上图中表示,顶点属性包括位置、纹理坐标、法向量、颜色等多个属性,每个属性的数据可以存放在不同的buffer中。我们可以根据需求,在程序中创建多个VBO和VAO。

使用VAO和VBO的伪代码如下(来自SO):

initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO

那么如何创建VBO和VAO呢? OpenGL中的对象创建和使用与C++中对象创建不一样,下面代码描述了在C++中创建和使用对象的方式(来自[Learning Modern 3D Graphics Programming]):

struct Object  
{  
    int count;  
    float opacity;  
    char *name;  
};  

//创建对象.  
Object newObject;  

// 设置对象的状态
newObject.count = 5;  
newObject.opacity = 0.4f;  
newObject.name = "Some String";

在OpenGL中创建和使用对象却类似这样:

//创建对象 不允许使用自定义名字  
GLuint objectName;  
glGenObject(1, &objectName);  

// 设置对象状态
glBindObject(GL_MODIFY, objectName);  
glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5);  
glObjectParameterf(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f);  
glObjectParameters(GL_MODIFY, GL_OBJECT_NAME, "Some String"); 

注意OpenGL中创建一个对象,由GLuint 类型来作为对象标识符,而不允许使用自定义名字,这样就不会导致对象重名了。在OpenGL中每个对象在使用前,要绑定到上下文对象,即所谓的target,例如上例中就是GL_MODIFY这个target。
Step1: 创建VBO 我们这样来创建:

    GLuint VBOId;
    glGenBuffers(1, &VBOId);

API void glGenBuffers( GLsizei n, GLuint * buffers);
这里n指定产生buffer的数目,而buffers则是标识符的地址。一次可以产生一个或者多个buffer.

Step2: 将顶点数据传送到VBO或者为VBO预分配空间。
本节我们绘制一个三角形,对于三角形要在3D空间中指定顶点,必定使用三维坐标。这个顶点坐标需要经过顶点着色器处理后最终才能用于生产三角形,这里面涉及到坐标转换等内容,本节不做深入探讨。经过坐标转换后,顶点坐标最终落在规范化设备坐标系(normalized device coordinate , NDC)中, NDC中坐标范围均为[-1,1],因此这里我们简化处理,将顶点坐标全部定在这个范围内,指定为:

GLfloat vertices[] = {
    -0.5f, 0.0f, 0.0f,
    0.5f, 0.0f, 0.0f,
    0.0f, 0.5f, 0.0f
};

将数据传送到GPU中需要通过函数glBufferData实现。

API void glBufferData( GLenum target,
GLsizeiptr size,
const GLvoid * data,
GLenum usage);
1.函数中target参数表示绑定的目标,包括像GL_ARRAY_BUFFER用于Vertex attributes(顶点属性),GL_ELEMENT_ARRAY_BUFFER用于索引绘制等目标。
2.size参数表示需要分配的空间大小,以字节为单位。
3.data参数用于指定数据源,如果data不为空将会拷贝其数据来初始化这个缓冲区,否则只是分配预定大小的空间。预分配空间后,后续可以通过glBufferSubData来更新缓冲区内容。
4.usage参数指定数据使用模式,例如GL_STATIC_DRAW指定为静态绘制,数据保持不变, GL_DYNAMIC_DRAW指定为动态绘制,数据会经常更新。

我们这里绘制一个静态的三角形,vertex attribute顶点属性这个概念包括顶点的位置、纹理坐标、法向量、颜色等属性数据,因此我们的顶点位置数据适合绑定到GL_ARRAY_BUFFER目标,同时数据在传送时初始化缓冲区,因此可以这样实现:

   glBindBuffer(GL_ARRAY_BUFFER, VBOId);
   glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

Step3: 通知OpenGL如何解释这个顶点属性数组
将数据传送到GPU后,我们还需要告知OpenGL如何解释这个数据,也就是告知其数据格式,因为从底层来看数据一个字节块而已。要通知OpenGL如何解释数据,要使用函数glVertexAttribPointer.

API void glVertexAttribPointer( GLuint index,
GLint size,
GLenum type,
GLboolean normalized,
GLsizei stride,
const GLvoid * pointer);
1. 参数index 表示顶点属性的索引 这个索引即是在顶点着色器中的属性索引,索引从0开始记起。
2. 参数size 每个属性数据由几个分量组成。例如上面顶点每个属性为3个float组成的,size即为3。分量的个数必须为1,2,3,4这四个值之一。
3. 参数type表示属性分量的数据类型,例如上面的顶点数据为float则填写GL_FLOAT.
4. 参数normalized 表示是否规格化,当存储整型时,如果设置为GL_TRUE,那么当被以浮点数形式访问时,有符号整型转换到[-1,1],无符号转换到[0,1]。否则直接转换为float型,而不进行规格化。
5. 参数stride表示连续的两个顶点属性之间的间隔,以字节大小计算。当顶点属性紧密排列(tightly packed)时,可以填0,由OpenGL代替我们计算出该值。
6. 参数pointer表示当前绑定到 GL_ARRAY_BUFFER缓冲对象的缓冲区中,顶点属性的第一个分量距离数据的起点的偏移量,以字节为单位计算。

上面这个函数是很重要的,刚接触时可能对多个参数感到厌烦,慢慢就会习惯。这里以上述包含顶点位置的属性数组为例,做一个图解(来自:learn opengl):
这里写图片描述
这里我们可以看出,调用上述函数时,属性索引为0(稍后着色器中会与之对应), 属性的分量个数为3,分量的数据类型为GL_FLOAT, normalized设为GL_FALSE, 参数stride为3*sizeof(GL_FLOAT)=12,
pointer的偏移量为0,但是要写为(GLvoid*)0(强制转换),具体如下所示:

glVertexAttribPointer(0, 3, GL_FLOAT, 
     3 * sizeof(GL_FLOAT), (GLvoid*)0);
glEnableVertexAttribArray(0);

关于glVertexAttribPointer函数中stride和offset函数的详细解释,你还可以参考我的另一篇关于buffer object的文章

这样我们创建了VBO,并将数据传送到GPU,并告知了OpenGL如何解析这些数据。在整个过程中,我们调用了很多函数,如果在以后绘制时好需要继续调用这些函数,那将会多么麻烦,因此这时候VAO就起到了关键作用。VAO能记录VBO的相关信息,在以后绘图时,只需要绑定对应的VAO就能找到这些状态,方便OPenGL使用。因此,在创建VBO这一过程中,我们要使用VAO来记录。方法便是,在所有VBO操作之前,先创建和绑定VAO。

绘制三角形时创建VAO和VBO的最终的代码如下:

    // 指定顶点属性数据 顶点位置
    GLfloat vertices[] = {
        -0.5f, 0.0f, 0.0f,
        0.5f, 0.0f, 0.0f,
        0.0f, 0.5f, 0.0f
    };
    // 创建缓存对象
    GLuint VAOId, VBOId;
    // Step1: 创建并绑定VAO对象
    glGenVertexArrays(1, &VAOId);
    glBindVertexArray(VAOId);
    // Step2: 创建并绑定VBO对象
    glGenBuffers(1, &VBOId);
    glBindBuffer(GL_ARRAY_BUFFER, VBOId);
    // Step3: 分配空间 传送数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // Step4: 指定解析方式  并启用顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // 解除绑定
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

在代码的最后,我们暂时解除绑定,能够防止后续操作干扰到了当前VAO和VBO。
现在在程序中使用VAO绘制三角形则只需要调用:

    glBindVertexArray(VAOId); // 使用VAO信息
    glUseProgram(shaderProgramId); // 使用着色器
    glDrawArrays(GL_TRIANGLES, 0, 3);

这里使用着色器,稍后介绍。glDrawArrays函数使用VBO数据绘制物体。其使用方法为:

API void glDrawArrays( GLenum mode,
GLint first,
GLsizei count);
1.mode 参数表示绘制的基本类型,OpenGL预制了 GL_POINTS, GL_LINE_STRIP等基本类型。一个复杂的图形,都是有这些基本类型构成的。
2.first表示启用的顶点属性数组中第一个数据的索引。
3.count表示绘制需要的顶点数目。

上述调用时我们选择GL_TRIANGLES表示绘制三角形,使用3个顶点。

着色器程序

目前我们主要使用顶点着色器和片元着色器。对于着色器,采用的是GLSL语言(OpenGL Shading Language)编写的程序,类似于C语言程序。
要使用着色器需要经历3个步骤:

  1. 创建和编译shader object
  2. 创建shader program,链接多个shader object到program
  3. 在绘制场景时启用shader program

具体流程如下图所示:

Created with Raphaël 2.1.0 着色器源文件(shader file) 读取源码(read source) 编译源码(compile source) 链接着色器对象(link shader objects) 着色器程序对象(program object)

以上是关于编程题目:利用Opengl绘制一个三角形。的主要内容,如果未能解决你的问题,请参考以下文章

Scratch画图100例图45-scratch绘制多层旋转三角形 少儿编程 scratch编程画图案例教程 考级比赛画图集训案例

OpenGL 未按预期绘制

卐 2-第一个OpenGL程序

scratch绘制多层旋转三角形 电子学会图形化编程scratch等级考试三级真题和答案解析2022年9月

Scratch创意绘图螺旋三角形 图形化编程Scratch等级考试四级编程题真题2020-9

Android OpenGL ES绘制三角形