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

如何不使用Java编写Android应用?C/C++帧缓冲访问实现求助

在Android Studio中用C/C++实现光线追踪图像输出(帧缓冲访问替代方案)

由于Android原生禁止直接访问系统帧缓冲(高版本无权限,低版本需root),且这种方式无法利用GPU合成能力,推荐用EGL + OpenGL ES的方案,在C/C++层完成光线追踪计算后,通过OpenGL将像素数据渲染到屏幕,完全绕开Java层的性能损耗。

步骤1:配置Android Studio NDK环境

在Module级build.gradle中开启NDK和CMake支持,同时开启最高优化:

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++17 -O3 -mfpu=neon" // 开启C++17、O3优化和NEON指令
                arguments "-DANDROID_STL=c++_shared"
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

步骤2:C/C++层实现EGL初始化与图像渲染

核心是通过EGL创建OpenGL上下文,将光线追踪生成的像素缓冲区作为纹理渲染到屏幕:

#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <android/native_window.h>
#include <cstdlib>

// 全局资源
EGLDisplay eglDisplay;
EGLContext eglContext;
EGLSurface eglSurface;
GLuint frameTexture;
int screenW, screenH;
GLuint shaderProgram;
GLuint vao;

// 编译着色器辅助函数(实际开发需补充错误检查)
static GLuint compileShader(GLenum type, const char* source) {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &source, nullptr);
    glCompileShader(shader);
    return shader;
}

// 初始化EGL与OpenGL
extern "C" JNIEXPORT jboolean JNICALL
Java_com_example_raytrace_MainActivity_initNative(JNIEnv* env, jobject thiz, jobject surface) {
    ANativeWindow* window = ANativeWindow_fromSurface(env, surface);

    // 初始化EGL
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(eglDisplay, nullptr, nullptr);

    const EGLint configAttribs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
        EGL_NONE
    };
    EGLConfig config;
    EGLint numConfigs;
    eglChooseConfig(eglDisplay, configAttribs, &config, 1, &numConfigs);

    eglSurface = eglCreateWindowSurface(eglDisplay, config, window, nullptr);
    ANativeWindow_release(window);

    const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
    eglContext = eglCreateContext(eglDisplay, config, EGL_NO_CONTEXT, contextAttribs);
    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

    // 获取屏幕尺寸
    eglQuerySurface(eglDisplay, eglSurface, EGL_WIDTH, &screenW);
    eglQuerySurface(eglDisplay, eglSurface, EGL_HEIGHT, &screenH);

    // 创建纹理存储光线追踪像素
    glGenTextures(1, &frameTexture);
    glBindTexture(GL_TEXTURE_2D, frameTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenW, screenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    // 编译全屏着色器
    const char* vertexShader = "#version 300 es\nin vec2 aPos;out vec2 texCoord;void main(){gl_Position=vec4(aPos,0.0,1.0);texCoord=(aPos+1.0)/2.0;}";
    const char* fragmentShader = "#version 300 es\nprecision mediump float;in vec2 texCoord;uniform sampler2D frameTex;out vec4 fragColor;void main(){fragColor=texture(frameTex,texCoord);}";

    GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShader);
    GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vs);
    glAttachShader(shaderProgram, fs);
    glLinkProgram(shaderProgram);
    glUseProgram(shaderProgram);
    glDeleteShader(vs);
    glDeleteShader(fs);

    // 设置全屏顶点数据
    float vertices[] = {-1.0f,-1.0f, 1.0f,-1.0f, -1.0f,1.0f, 1.0f,1.0f};
    GLuint vbo;
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), nullptr);
    glEnableVertexAttribArray(0);

    return JNI_TRUE;
}

// 更新纹理并渲染(替换随机像素为你的光线追踪计算逻辑)
extern "C" JNIEXPORT void JNICALL
Java_com_example_raytrace_MainActivity_renderNative(JNIEnv* env, jobject thiz) {
    // 1. 生成RGBA像素数据(示例用随机值)
    uint8_t* pixelData = new uint8_t[screenW * screenH * 4];
    for(int i=0; i<screenW*screenH*4; i+=4){
        pixelData[i] = rand()%255;     // R
        pixelData[i+1] = rand()%255;   // G
        pixelData[i+2] = rand()%255;   // B
        pixelData[i+3] = 255;          // A
    }

    // 2. 更新纹理数据
    glBindTexture(GL_TEXTURE_2D, frameTexture);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenW, screenH, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
    delete[] pixelData;

    // 3. 渲染到屏幕
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(vao);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    eglSwapBuffers(eglDisplay, eglSurface);
}

// 释放资源
extern "C" JNIEXPORT void JNICALL
Java_com_example_raytrace_MainActivity_cleanupNative(JNIEnv* env, jobject thiz) {
    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroySurface(eglDisplay, eglSurface);
    eglDestroyContext(eglDisplay, eglContext);
    eglTerminate(eglDisplay);
    glDeleteTextures(1, &frameTexture);
    glDeleteVertexArrays(1, &vao);
}

步骤3:Java层传递Surface到Native层

用SurfaceView获取渲染表面,将其传给C/C++层初始化EGL:

import android.app.Activity;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends Activity {
    static {
        System.loadLibrary("raytrace");
    }

    private native boolean initNative(Object surface);
    private native void renderNative();
    private native void cleanupNative();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SurfaceView surfaceView = new SurfaceView(this);
        setContentView(surfaceView);

        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                initNative(holder.getSurface());
                // 启动渲染线程,避免阻塞UI
                new Thread(() -> {
                    while (!Thread.currentThread().isInterrupted()) {
                        renderNative();
                        try {
                            Thread.sleep(16); // 控制帧率
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }).start();
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                cleanupNative();
            }
        });
    }
}

性能优化要点

  • 多线程并行计算:用C++11std::thread或OpenMP(CMake加-fopenmp编译选项)拆分光线追踪任务
  • 内存对齐:用aligned_alloc分配像素缓冲区,对齐到16字节适配NEON指令
  • 避免跨层拷贝:直接在Native堆生成像素数据,不要通过Java层传递

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 05:18:19