在 Lazarus 中学习 OpenGL

在 Lazarus 中学习 OpenGL

教学网站

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

API 查询

https://docs.gl/

创建窗口

Lazarus 可以在各个平台创建窗口,但这些窗口不包含 OpenGL 的上下文,所以这里借助 GLFW 来创建窗口和 OpenGL 上下文。GLFW 是一个专门针对 OpenGL 的 C 语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建 OpenGL 上下文、定义窗口参数以及处理用户输入。

GLFW

下载地址:https://www.glfw.org/download.html

下载源码并解压,然后进入解压后的目录,使用下面的脚本进行编译(Linux 环境):

#!/bin/sh

cmake -S . -B build
cd build
make

编译完毕后,在解压目录的 build/src 目录中找到 libglfw3.a 拿出来用即可。

GLFW 是用 C 语言编写的,要在 Lazarus 中使用,必须要有 Free Pascal 版本的函数绑定,在 Github 上有人维护了一份 GLFW 的 Free Pascal 绑定,可以从 https://github.com/Blueicaro/GLFW 下载它(只需要下载 Glfw33 目录中的 glfw.pas 即可)。

你好世界

在 Lazarus 的“工程”菜单中选择“新建工程”,在弹出的对话框中选择“简单程序”,然后点击“确定”。然后将该工程保存为“project1.lpi”(.lpi 是工程文件,同时还会生成 .lpr 程序源文件、.lps 设置文件、.res 资源文件,后两个文件我们用不到,可以删掉,但每次保存时都会自动生成这些文件)。然后在这些文件的旁边创建两个目录 Libs 和 Units,然后将 libglfw3.a 放入 Libs 目录中,并改名为 libGLFW3.a(因为在 glfw.pas 中,该库文件的名称被定义成了大写格式),将 glfw.pas 放入 Units 目录中。

然后在 Lazarus 的“工程”菜单中选择“工程选项”,在弹出的对话框中定位到“编译器选项->路径”选项卡,将“其它单元文件(-Fu)”的路径设置为 Units,这样我们就可以使用 glfw.pas 了。然后将“库(-Fl)”的路径设置为 Libs,这样在编译时就可以链接到 libGLFW.a 了。然后点击“确定”(忽略“非 ASCII”之类的警告)。再一次保存工程,将我们的设置信息保存起来(它只对当前工程有效)。

然后修改源代码为如下内容:

program OpenGL;

// 使用 ObjFPC 模式,功能更强大,启用长字符串支持。
{$mode objfpc}{$H+}

// libGLFW.a 需要动态链接 glibc 的数学库。
{$linklib libm.so}

// 使用刚下载的 glfw.pas,在 Linux 中,它依赖 Xlib。
// gl    中包含了基本的(旧)OpenGL 函数。
// glext 中包含了扩展的(新)OpenGL 函数。
uses
  glfw, Xlib, gl, glext;

var
  window: pGLFWwindow;

begin
  // 初始化 GLFW(返回 0 表示初始化失败)
  if glfwInit() = 0 then Halt(-1);

  // 告诉 GLFW 我们要使用的 OpenGL 3.3 版,这样 GLFW 在创建 OpenGL 上下文时会做出适当调整。
  // 这也可以确保用户在没有适当的 OpenGL 版本支持的情况下无法运行。
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  // 告诉 GLFW 我们使用的是核心模式,只使用 OpenGL 功能的一个子集(没有向后兼容特性)。
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  // 创建窗口及其 OpenGL 上下文
  window := glfwCreateWindow(800, 600, '你好,世界!', nil, nil);
  if window = nil then begin
    glfwTerminate();
    Halt(-1);
  end;

  // 使窗口的上下文成为当前,必须在设置上下文之后,才能使用 OpenGL 的函数
  glfwMakeContextCurrent(window);

  // 加载所需版本的 OpenGL 函数
  Load_GL_VERSION_3_3();

  // glfwWindowShouldClose 函数用于检查 GLFW 是否被要求退出
  while glfwWindowShouldClose(window) = 0 do begin
      // 清屏
      glClear(GL_COLOR_BUFFER_BIT);

      // 交换前后缓冲区
      glfwSwapBuffers(window);

      // 检查有没有触发事件或更新窗口状态,并调用相应的回调函数
      glfwPollEvents();
  end;

  // 销毁之前创建的所有资源
  glfwTerminate();
end.

然后按 F9 运行程序即可。

教程中的例子(空白窗口)

program OpenGL;

{$mode objfpc}{$H+}
{$linklib libm.so}

uses
  glfw, Xlib, gl, glext;

var
  window: pGLFWwindow;

// 当窗口大小改变时要调用的回调函数
procedure FramebufferSizeCallback(window: PGLFWwindow; width, height: GLSizei); cdecl;
begin
    glViewport(0, 0, width, height);
end;

// 键盘事件处理函数
procedure processInput(window: PGLFWwindow);
begin
  if glfwGetKey(window, GLFW_KEY_ESCAPE) = GLFW_PRESS then
    glfwSetWindowShouldClose(window, GLFW_INT(True));
end;

begin
  if glfwInit() = 0 then begin
    WriteLn(stderr, '初始化 GLFW 失败');
    Halt(-1);
  end;

  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  window := glfwCreateWindow(800, 600, '', nil, nil);
  if window = nil then begin
    WriteLn(stderr, '创建 GLFW 窗口失败');
    glfwTerminate();
    Halt(-1);
  end;

  glfwMakeContextCurrent(window);

  // 设置当窗口尺寸改变时需要调用的回调函数
  glfwSetFramebufferSizeCallback(window, @FramebufferSizeCallback);

  // 获取 OpenGL 版本,显示在标题栏中
  glfwSetWindowTitle(window, glGetString(GL_VERSION));

  Load_GL_VERSION_3_3();

  while glfwWindowShouldClose(window) = 0 do begin
      // 处理键盘事件
      processInput(window);

      // 用指定颜色清屏
      glClearColor(0.2, 0.3, 0.3, 1.0);
      glClear(GL_COLOR_BUFFER_BIT);

      glfwSwapBuffers(window);
      glfwPollEvents();
  end;
  glfwTerminate();
end.

教程中的例子(三角形)

program OpenGL;

{$mode objfpc}{$H+}
{$linklib libm.so}

uses
  glfw, Xlib, gl, glext;

var
  window: pGLFWwindow;

procedure FramebufferSizeCallback(window: PGLFWwindow; width, height: GLSizei); cdecl;
begin
    glViewport(0, 0, width, height);
end;

procedure processInput(window: PGLFWwindow);
begin
  if glfwGetKey(window, GLFW_KEY_ESCAPE) = GLFW_PRESS then
    glfwSetWindowShouldClose(window, GLFW_INT(True));
end;

var
  vertexShader  : Cardinal;
  fragmentShader: Cardinal;
  shaderProgram : Cardinal;
  success       : Integer;
  infoLog       : String;

  vertices: array of single = (
    -0.5, -0.5, 0.0,
     0.5, -0.5, 0.0,
     0.0,  0.5, 0.0
  );

  VBO: Cardinal;
  VAO: Cardinal;

const
  vertexShaderSource: String = '#version 330 core'#10+
    'layout (location = 0) in vec3 aPos;'#10+
    'void main()'#10+
    '{'#10+
    '   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);'#10+
    '}';

  fragmentShaderSource: PChar = '#version 330 core'#10+
    'out vec4 FragColor;'#10+
    'void main()'#10+
    '{'#10+
    '    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);'#10+
    '}';

begin
  if glfwInit() = 0 then begin
    WriteLn(stderr, '初始化 GLFW 失败');
    Halt(-1);
  end;

  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  window := glfwCreateWindow(800, 600, '', nil, nil);
  if window = nil then begin
    WriteLn(stderr, '创建 GLFW 窗口失败');
    glfwTerminate();
    Halt(-1);
  end;

  glfwMakeContextCurrent(window);
  glfwSetFramebufferSizeCallback(window, @FramebufferSizeCallback);
  glfwSetWindowTitle(window, glGetString(GL_VERSION));

  Load_GL_VERSION_3_3();

  vertexShader := glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, @vertexShaderSource, nil);
  glCompileShader(vertexShader);

  SetLength(infoLog, 512);
  glGetShaderiv(vertexShader, GL_COMPILE_STATUS, @success);
  if success = 0 then begin
    glGetShaderInfoLog(vertexShader, 512, nil, PChar(infoLog));
    WriteLn(stderr, 'ERROR::SHADER::VERTEX::COMPILATION_FAILED'#10, infoLog, #10);
  end;

  fragmentShader := glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, @fragmentShaderSource, nil);
  glCompileShader(fragmentShader);

  glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, @success);
  if success = 0 then begin
    glGetShaderInfoLog(vertexShader, 512, nil, PChar(infoLog));
    WriteLn(stderr, 'ERROR::SHADER::FRAGMENT::COMPILATION_FAILED'#10, infoLog, #10);
  end;

  shaderProgram := glCreateProgram();
  glAttachShader(shaderProgram, vertexShader);
  glAttachShader(shaderProgram, fragmentShader);
  glLinkProgram(shaderProgram);

  glGetShaderiv(shaderProgram, GL_COMPILE_STATUS, @success);
  if success = 0 then begin
    glGetShaderInfoLog(shaderProgram, 512, nil, PChar(infoLog));
    WriteLn(stderr, 'ERROR::SHADER::PROGRAM::LINKING_FAILED'#10, infoLog, #10);
  end;

  glDeleteShader(vertexShader);
  glDeleteShader(fragmentShader);

  glGenVertexArrays(1, @VAO);
  glGenBuffers(1, @VBO);
  glBindVertexArray(VAO);

  glBindBuffer(GL_ARRAY_BUFFER, VBO);
  glBufferData(GL_ARRAY_BUFFER, Length(vertices) * sizeof(single), Pointer(vertices), GL_STATIC_DRAW);

  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(Single), Pointer(0));
  glEnableVertexAttribArray(0);

  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindVertexArray(0);

  while glfwWindowShouldClose(window) = 0 do begin
    processInput(window);

    glClearColor(0.2, 0.3, 0.3, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glfwSwapBuffers(window);
    glfwPollEvents();
  end;

  glDeleteVertexArrays(1, @VAO);
  glDeleteBuffers(1, @VBO);
  glDeleteProgram(shaderProgram);

  glfwTerminate();
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值