You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cl-opengl编写OBJ加载器时glDrawElements无法渲染问题

问题诊断与修复方案

我仔细排查了你的代码,发现几个核心问题导致渲染空白,下面逐个拆解并给出修复方案:

1. 全局变量引用错误

你定义了全局变量*vertices*和*elements*,但在load-obj函数里直接使用了无星号的vertices和elements——在Common Lisp中,这会被视为未绑定的局部变量,导致顶点和元素数据根本没被正确加载到全局数组里。

修复:把load-obj里的vertices替换为*vertices*,elements替换为*elements*。

2. 顶点属性指针参数错误

你的顶点是3维向量(vec3),但gl:vertex-attrib-pointer的第二个参数设成了4,这会让OpenGL每次读取4个float作为一个顶点,直接导致顶点数据错位,最终物体被渲染到视锥体之外,屏幕自然显示空白。

修复:将(gl:vertex-attrib-pointer 0 4 :float nil 0 0)修改为(gl:vertex-attrib-pointer 0 3 :float nil 0 0)。

3. glDrawElements调用错误

你调用(gl:draw-elements :triangles glarray-2)的方式完全错误:glDrawElements的第二个参数应该是元素的数量(整数),而非GL数组对象,同时必须指定元素的类型。

修复:改成(gl:draw-elements :triangles (length *elements*) :unsigned-short 0)。

4. SDL OpenGL上下文配置错误

你设置(sdl2:gl-set-attr :context-profile-mask 0)不符合OpenGL 3.3核心上下文的要求,正确的掩码应该是核心模式,否则可能无法创建符合规范的上下文,导致着色器或渲染功能异常。

修复:将该行修改为(sdl2:gl-set-attr :context-profile-mask sdl2-ffi::+SDL-GL-CONTEXT-PROFILE-CORE+)。

5. 其他细节优化

  • load-obj里的文件打开可以用更简洁安全的with-open-file,避免手动处理流的问题;
  • 别忘了释放glarray-2这个GL数组,避免内存泄漏;
  • 添加程序链接日志打印,方便排查着色器链接阶段的问题。

修正后的完整代码

(require :cl-opengl)
(require :sdl2)

(defvar *vertices* (make-array 9 :fill-pointer 0))
(defvar *elements* (make-array 9 :fill-pointer 0))

(defvar *vertex-shader* " #version 330 core
layout (location = 0) in vec3 aPos;
void main() {
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
")

(defvar *fragment-shader* " #version 330 core
out vec4 FragColor;
void main() {
    FragColor = vec4(0.95f, 0.98f, 0.65f, 1.0f);
}
")

(defun split-str-1 (string &optional (separator " ") (r nil))
  (let ((n (position separator string :from-end t :test #'(lambda (x y) (find y x :test #'string=)))))
    (if n
        (split-str-1 (subseq string 0 n) separator (cons (subseq string (1+ n)) r))
        (cons string r))))

(defun split-str (string &optional (separator " "))
  (split-str-1 string separator))

(defun parse-float (number)
  (with-input-from-string (in number)
    (read in)))

(defun load-obj (file-name)
  (with-open-file (source file-name)
    (loop for line = (read-line source nil nil) while line do
      (let* ((split-line (split-str line " "))
             (header (car split-line))
             (rest (cdr split-line)))
        (cond
          ((string= header "v")
           (dolist (vertex rest)
             (vector-push-extend (parse-float vertex) *vertices*)))
          ((string= header "f")
           (dolist (face rest)
             (let ((element (parse-integer (car (split-str face "/")))))
               (vector-push-extend (- element 1) *elements*)))))))))

(defun main ()
  (load-obj "tortoise.obj")
  (sdl2:with-init (:everything)
    (sdl2:gl-set-attr :context-profile-mask sdl2-ffi::+SDL-GL-CONTEXT-PROFILE-CORE+)
    (sdl2:gl-set-attr :context-major-version 3)
    (sdl2:gl-set-attr :context-minor-version 3)
    (sdl2:with-window (win :width 800 :height 600 :flags `(:shown :opengl))
      (sdl2:with-gl-context (gl-context win)
        (sdl2:gl-make-current win gl-context)
        (gl:viewport 0 0 800 600)
        (gl:clear-color 0.957 0.376 0.286 1.0)
        (let ((glarray (gl:alloc-gl-array :float (length *vertices*)))
              (glarray-2 (gl:alloc-gl-array :unsigned-short (length *elements*))))
          (dotimes (i (length *elements*))
            (setf (gl:glaref glarray-2 i) (aref *elements* i)))
          (dotimes (i (length *vertices*))
            (setf (gl:glaref glarray i) (aref *vertices* i)))
          (let ((vbo (gl:gen-buffer))
                (vao (gl:gen-vertex-array))
                (ebo (gl:gen-buffer)))
            (gl:bind-vertex-array vao)
            (gl:bind-buffer :array-buffer vbo)
            (gl:buffer-data :array-buffer :static-draw glarray)
            (gl:free-gl-array glarray)
            (gl:bind-buffer :element-array-buffer ebo)
            (gl:buffer-data :element-array-buffer :static-draw glarray-2)
            (gl:free-gl-array glarray-2)
            (gl:vertex-attrib-pointer 0 3 :float nil 0 0)
            (gl:enable-vertex-attrib-array 0)
            (let ((vertex-shader (gl:create-shader :vertex-shader))
                  (fragment-shader (gl:create-shader :fragment-shader)))
              (gl:shader-source vertex-shader *vertex-shader*)
              (gl:shader-source fragment-shader *fragment-shader*)
              (gl:compile-shader vertex-shader)
              (gl:compile-shader fragment-shader)
              (print (gl:get-shader-info-log vertex-shader))
              (print (gl:get-shader-info-log fragment-shader))
              (let ((program (gl:create-program)))
                (gl:attach-shader program vertex-shader)
                (gl:attach-shader program fragment-shader)
                (gl:link-program program)
                (print (gl:get-program-info-log program))
                (gl:delete-shader vertex-shader)
                (gl:delete-shader fragment-shader)
                (gl:use-program program)))
            (sdl2:with-event-loop (:method :poll)
              (:idle ()
               (gl:clear :color-buffer)
               (gl:bind-vertex-array vao)
               (gl:draw-elements :triangles (length *elements*) :unsigned-short 0)
               (gl:flush)
               (sdl2:gl-swap-window win))
              (:quit () t))))))))

如果修复后仍然空白,建议检查你的OBJ模型坐标是否在OpenGL标准化设备坐标(NDC)范围内(x/y/z值在[-1,1]之间)。如果模型尺寸过大,需要添加投影和视图矩阵来将模型缩放到可见区域。

内容的提问来源于stack exchange,提问作者PavelF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:08:22