You need to enable JavaScript to run this app. Android Studio基于OpenGL 1.0实现从中心左右移动的无畸变圆绘制
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio基于OpenGL 1.0实现从中心左右移动的无畸变圆绘制

问题根源分析

图形畸变原因

  • 第一段代码的比例修正逻辑完全失效:
    • 硬编码width=100、height=50算出的ratio=2,和实际屏幕宽高比完全不匹配;同时在onSurfaceChanged中同时调用GLU.gluPerspective和gl.glOrthof,后调用的正交投影会覆盖透视投影配置,没有形成稳定的投影映射。
    • 自定义DrawCircle类的流程残缺:draw()方法仅生成顶点数组,既没有将顶点传入OpenGL缓冲区,也没有触发实际的绘制指令;同时类中的顶点索引变量c没有在每次绘制前重置为0,多次调用后会出现数组越界、顶点数据错乱的问题,根本无法生成有效图形。
  • 第二段代码的畸变来源:
    • 透视投影参数配置错误:设置GLU.gluPerspective时近裁剪面设为-1、远裁剪面设为-10,不符合OpenGL近远裁剪面必须为正值的规则;同时没有为x、y轴设置等比例的可视映射范围,x方向和y方向的单位长度在屏幕上的实际占比不匹配,最终正圆被拉伸为椭圆。

动画实现失败原因

  • 代码全程没有维护圆形的位置状态变量,没有每帧更新位置的逻辑,也没有边界检测和方向翻转的处理流程,缺少动画运行的核心驱动逻辑。
可行实现方案

核心修正思路

  • 采用正交投影替代错误的透视/混用投影配置,在onSurfaceChanged中根据真实屏幕宽高比设置等比例的可视范围,保证x、y方向的单位长度在屏幕上完全相等,从根源上避免图形畸变。
  • 正圆的顶点坐标仅在初始化时生成一次,避免每帧重复分配内存造成性能损耗。
  • 独立维护圆形的中心x坐标、移动方向参数,在每帧绘制前更新坐标,检测到边界时翻转移动方向,实现往返移动效果。

完整可运行代码

package pl.varkame;

import android.opengl.GLSurfaceView;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyRenderer implements GLSurfaceView.Renderer {
    // 圆形顶点配置
    private final int SEG_COUNT = 720;
    private FloatBuffer circleVertexBuffer;
    private final float circleRadius = 0.1f;
    // 移动动画配置
    private float circleX = 0.0f;
    private float moveSpeed = 0.01f;
    private int moveDirection = 1; // 1为向右,-1为向左
    private float maxXOffset = 0.0f; // 可移动的x轴最大偏移量

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 初始化正圆顶点:x、y方向半径完全相等,无畸变
        float[] vertices = new float[SEG_COUNT * 2 + 2];
        int index = 0;
        // TRIANGLE_FAN模式需要第一个点为圆心,才能正确生成扇形填充
        vertices[index++] = 0.0f;
        vertices[index++] = 0.0f;
        for (int i = 0; i < SEG_COUNT; i++) {
            double rad = Math.PI * 2 * i / SEG_COUNT;
            vertices[index++] = (float) Math.cos(rad) * circleRadius;
            vertices[index++] = (float) Math.sin(rad) * circleRadius;
        }
        // 把顶点数据传入OpenGL直接缓冲区
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
        vbb.order(ByteOrder.nativeOrder());
        circleVertexBuffer = vbb.asFloatBuffer();
        circleVertexBuffer.put(vertices);
        circleVertexBuffer.position(0);

        // 配置背景为绿色
        gl.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        // 计算真实屏幕宽高比,设置等比例正交投影
        float screenRatio = (float) width / (float) height;
        gl.glOrthof(-screenRatio, screenRatio, -1.0f, 1.0f, -1.0f, 1.0f);
        // 计算可移动的最大x偏移:保证圆形完全处于屏幕可视范围内
        maxXOffset = screenRatio - circleRadius;
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // 更新圆形位置
        circleX += moveSpeed * moveDirection;
        // 检测到达边界,翻转移动方向实现往返效果
        if (circleX > maxXOffset || circleX < -maxXOffset) {
            moveDirection = -moveDirection;
        }

        // 清屏
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        // 设置圆形颜色为蓝色
        gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
        // 重置模型视图矩阵,应用圆形的x轴偏移
        gl.glLoadIdentity();
        gl.glTranslatef(circleX, 0.0f, 0.0f);

        // 提交顶点数据绘制圆形
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, circleVertexBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, SEG_COUNT + 1);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    }

实现效果说明

  • 投影配置为等比例正交映射,绘制的圆形不会出现椭圆畸变
  • 圆形初始位置为屏幕中心,沿x轴向右移动到屏幕右侧边界后自动折返,向左移动到屏幕左侧边界后再次折返,实现要求的往返动画
  • 圆形顶点仅初始化一次,每帧无需重新计算顶点数组,性能开销更低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 09:54:06