上篇文章我们讲到了如何构建一个OpenGL3.0的android开发环境,这次我们来实现一个简单的三角形。
下面是一些需要提前了解的知识点:
- 顶点着色器
- 片段着色器
- OpenGL ES着色语言
- 程序
可以通过阅读《OpenGL ES 3.0 编程指南》来了解这些知识~~,本文参考自https://mp.weixin.qq.com/s/8AGsjStVUa4WIwlCxjnzZA
下面直接进入主题:
1.首先我们需要一个native和java充当桥梁的类,来辅助绘制。
public class MyNativeRender {
public native void native_OnInit();
public native void native_OnUnInit();
public native void native_SetImageData(int format, int width, int height, byte[] bytes);
public native void native_OnSurfaceCreated();
public native void native_OnSurfaceChanged(int width, int height);
public native void native_onDrawFrame();
}
2.还需要一个自定义的GLSurfaceView和自定义的Renderer。
public class MyGLSurfaceView extends GLSurfaceView {
private static final String TAG = "com.example.opengl1.MyGLSurfaceView";
private MyNativeRender myNativeRender;
private MyGLRender myGLRender;
public MyGLSurfaceView(Context context) {
super(context);
}
public MyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setEGLContextClientVersion(3);
myNativeRender = new MyNativeRender();
myGLRender = new MyGLRender(myNativeRender);
setRenderer(myGLRender);
setRenderMode(RENDERMODE_CONTINUOUSLY);
}
public static class MyGLRender implements Renderer {
private MyNativeRender myNativeRender;
MyGLRender(MyNativeRender nativeRender){
myNativeRender = nativeRender;
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
Log.d(TAG, "onSurfaceCreated() called with: gl = [" + gl10 + "], config = [" + eglConfig + "]");
myNativeRender.native_OnSurfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
Log.d(TAG, "onSurfaceChanged() called with: width = [" + width + "], height = [" + height + "]");
myNativeRender.native_OnSurfaceChanged(width, height);
}
@Override
public void onDrawFrame(GL10 gl10) {
Log.d(TAG, "onDrawFrame() called with: gl10 = [" + gl10 + "]");
myNativeRender.native_onDrawFrame();
}
}
}
ps:接下来就开始native层的代码啦
3.我们先定义一个工具类来构造着色器
头文件:
#ifndef TRIANGLE_SAMPLE_H
#include <stdlib.h>
#include <GLES3/gl3.h>
GLuint LoadShader(GLenum shaderType, const char *pSource);
GLuint CreateProgram(const char *pVertexShaderSource, const char *pFragShaderSource, GLuint &vertexShaderHandle, GLuint &fragShaderHandle);
void DeleteProgram(GLuint &program);
void CheckGLError(const char *pGLOperation);
#endif
cpp:
#include "GLUtils.h"
#include <stdlib.h>
#include <GLES3/gl3.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
using namespace std;
GLuint LoadShader(GLenum shaderType, const char *pSource)
{
GLuint shader = 0;
shader = glCreateShader(shaderType); //创建着色器(两种类型:顶点着色器和片段着色器)
if (shader)
{
glShaderSource(shader, 1, &pSource, NULL); // 加载着色器程序
glCompileShader(shader); //编译
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); // 取编译结果,用来判断是否编译成功
if (!compiled) // 不成功
{
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen)
{
char* buf = (char*) malloc((size_t)infoLen);
if (buf)
{
glGetShaderInfoLog(shader, infoLen, NULL, buf);
free(buf);
}
glDeleteShader(shader);
shader = 0;
}
}
}
return shader;
}
GLuint CreateProgram(const char *pVertexShaderSource, const char *pFragShaderSource, GLuint &vertexShaderHandle, GLuint &fragShaderHandle)
{
GLuint program = 0;
vertexShaderHandle = LoadShader(GL_VERTEX_SHADER, pVertexShaderSource);
if (!vertexShaderHandle) return program;
fragShaderHandle = LoadShader(GL_FRAGMENT_SHADER, pFragShaderSource);
if (!fragShaderHandle) return program;
program = glCreateProgram(); //创建一个程序对象
if (program)
{
glAttachShader(program, vertexShaderHandle); //绑定程序与着色器程序
CheckGLError("glAttachShader");
glAttachShader(program, fragShaderHandle);
CheckGLError("glAttachShader");
glLinkProgram(program);
GLint linkStatus = GL_FALSE;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
glDetachShader(program, vertexShaderHandle);
glDeleteShader(vertexShaderHandle);
vertexShaderHandle = 0;
glDetachShader(program, fragShaderHandle);
glDeleteShader(fragShaderHandle);
fragShaderHandle = 0;
if (linkStatus != GL_TRUE)
{
GLint bufLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
if (bufLength)
{
char* buf = (char*) malloc((size_t)bufLength);
if (buf)
{
glGetProgramInfoLog(program, bufLength, NULL, buf);
free(buf);
}
}
glDeleteProgram(program);
program = 0;
}
}
return program;
}
void DeleteProgram(GLuint &program)
{
if (program)
{
glUseProgram(0);
glDeleteProgram(program);
program = 0;
}
}
void CheckGLError(const char *pGLOperation)
{
for (GLint error = glGetError(); error; error = glGetError())
{
}
}
4.接下来是绘制三角形的相关代码:
头文件:
#ifndef TRIANGLE_SAMPLE_H
#define TRIANGLE_SAMPLE_H
#include <stdlib.h>
#include <GLES3/gl3.h>
#include "EGL/egl.h"
#include "android/window.h"
#include "GLUtils.h"
class TriangleSample {
public :
GLuint m_ProgramObj;
GLuint m_VertexShader;
GLuint m_FragmentShader;
public :
void Init();
void Draw();
TriangleSample();
~TriangleSample();
};
#endif
cpp:
//
// Created by ByteFlow on 2019/7/9.
//
#include "GLUtils.h"
#include "TriangleSample.h"
#include "LogUtil.h"
using namespace std;
TriangleSample::TriangleSample()
{
}
TriangleSample::~TriangleSample()
{
if (m_ProgramObj)
{
glDeleteProgram(m_ProgramObj);
}
}
void TriangleSample::Init()
{
char vShaderStr[] =
"#version 300 es \n" //GL版本
"layout(location = 0) in vec4 vPosition; \n" // 输入变量(图形的顶点位置)
"void main() \n"
"{ \n"
" gl_Position = vPosition; \n" // 将输入变量赋值给gl_position
"} \n";
char fShaderStr[] =
"#version 300 es \n" //GL 版本
"precision mediump float; \n" //精度限定符号
"out vec4 fragColor; \n" //输出变量(颜色)
"void main() \n"
"{ \n"
" fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n" //等价于红色
"} \n";
m_ProgramObj = CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
}
void TriangleSample::Draw()
{
GLfloat vVertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
if(m_ProgramObj == 0)
return;
// Use the program object
glUseProgram (m_ProgramObj);
// Load the vertex data
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
glEnableVertexAttribArray (0);
glDrawArrays (GL_TRIANGLES, 0, 3);
}
5.我们定义一个操作绘制的中间类。
头文件:
#ifndef MY_GLRENDER_CONTEXT_H
#define MY_GLRENDER_CONTEXT_H
#include "TriangleSample.h"
#include "TextureMapSample.h"
using namespace std;
struct NativeImage {
int format;
int width;
int height;
uint8_t** ppPlane;
};
class MyGLRenderContext {
public :
TriangleSample m_Sample;
TextureMapSample* m_TextureSample;
NativeImage m_NativeImage;
static MyGLRenderContext* m_pContext;
public :
MyGLRenderContext(const MyGLRenderContext &mPContext);
MyGLRenderContext();
~MyGLRenderContext();
void SetImageData(int format, int width, int height, uint8_t *pData);
void OnSurfaceCreated();
void OnSurfaceChanged(int width, int height);
void OnDrawFrame();
static MyGLRenderContext *GetInstance();
static void DestroyInstance();
};
#endif
cpp:
#include "TriangleSample.h"
#include "MyGLRenderContext.h"
#include "LogUtil.h"
MyGLRenderContext* MyGLRenderContext::m_pContext = nullptr;
MyGLRenderContext::MyGLRenderContext()
{
m_TextureSample = new TextureMapSample();
}
MyGLRenderContext::~MyGLRenderContext()
{
if(m_TextureSample) {
delete m_TextureSample;
}
}
void MyGLRenderContext::SetImageData(int format, int width, int height, uint8_t *pData)
{
RenderImage* renderImage = new RenderImage;
renderImage->ppPlane = new uint8_t*[1];
renderImage->width = width;
renderImage->height = height;
renderImage->ppPlane[0] = pData;
m_TextureSample->setImage(renderImage);
/*switch (format)
{
case IMAGE_FORMAT_NV12:
case IMAGE_FORMAT_NV21:
nativeImage.ppPlane[1] = nativeImage.ppPlane[0] + width * height;
break;
case IMAGE_FORMAT_I420:
nativeImage.ppPlane[1] = nativeImage.ppPlane[0] + width * height;
nativeImage.ppPlane[2] = nativeImage.ppPlane[1] + width * height / 4;
break;
default:
break;
}*/
//m_TextureMapSample->LoadImage(&nativeImage);
}
void MyGLRenderContext::OnSurfaceCreated()
{
glClearColor(1.0f,1.0f,0.5f, 1.0f);
m_Sample.Init();
}
void MyGLRenderContext::OnSurfaceChanged(int width, int height)
{
glViewport(0, 0, width, height);
}
void MyGLRenderContext::OnDrawFrame()
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
m_Sample.Draw();
}
MyGLRenderContext *MyGLRenderContext::GetInstance()
{
if (m_pContext == nullptr)
{
m_pContext = new MyGLRenderContext();
}
return m_pContext;
}
void MyGLRenderContext::DestroyInstance()
{
if (m_pContext)
{
delete m_pContext;
m_pContext = nullptr;
}
}
6.最后就是我们jni代码啦。
头文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <jni.h>
/* Header for class com_example_opengl1_MyNativeRender */
#ifndef _Included_com_example_opengl1_MyNativeRender
#define _Included_com_example_opengl1_MyNativeRender
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif
extern "C"
cpp:
#include "MyNativeRender.h"
#include "MyGLRenderContext.h"
#include "jni.h"
using namespace std;
#define NATIVE_RENDER_CLASS_NAME "com/byteflow/app/MyNativeRender"
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL
Java_com_example_testndk_MyNativeRender_native_1OnInit(JNIEnv *env, jobject thiz) {
// TODO: implement native_OnInit()
MyGLRenderContext::GetInstance();
}
JNIEXPORT void JNICALL
Java_com_example_testndk_MyNativeRender_native_1OnUnInit(JNIEnv *env, jobject thiz)
{
MyGLRenderContext::DestroyInstance();
}
JNIEXPORT void JNICALL Java_com_example_testndk_MyNativeRender_native_1SetImageData
(JNIEnv *env, jobject instance, jint format, jint width, jint height, jbyteArray imageData)
{
int len = env->GetArrayLength (imageData);
uint8_t* buf = new uint8_t[len];
env->GetByteArrayRegion(imageData, 0, len, reinterpret_cast<jbyte*>(buf));
printf("stx-----start");
MyGLRenderContext::GetInstance()->SetImageData(format, width, height, buf);
printf("stx-----end");
delete[] buf;
env->DeleteLocalRef(imageData);
}
JNIEXPORT void JNICALL Java_com_example_testndk_MyNativeRender_native_1OnSurfaceCreated(JNIEnv *env, jobject instance)
{
MyGLRenderContext::GetInstance()->OnSurfaceCreated();
}
JNIEXPORT void JNICALL Java_com_example_testndk_MyNativeRender_native_1OnSurfaceChanged
(JNIEnv *env, jobject instance, jint width, jint height)
{
MyGLRenderContext::GetInstance()->OnSurfaceChanged(width, height);
}
JNIEXPORT void JNICALL Java_com_example_testndk_MyNativeRender_native_1onDrawFrame(JNIEnv *env, jobject instance)
{
MyGLRenderContext::GetInstance()->OnDrawFrame();
}
#ifdef __cplusplus
}
#endif
最后一定要记得将相关类加入到CMakeLists.txt中!!!
cmake_minimum_required(VERSION 3.18.1)
project("testndk")
add_library( # Sets the name of the library.
testndk
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp
MyNativeRender.h
MyNativeRender.cpp
MyGLRenderContext.h
MyGLRenderContext.cpp
TriangleSample.h
TriangleSample.cpp
GLUtils.h
GLUtils.cpp
)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
target_link_libraries( # Specifies the target library.
testndk
EGL
GLESv3
android
# Links the target library to the log library
# included in the NDK.
${log-lib})
下面是运行截图:

8万+

被折叠的 条评论
为什么被折叠?



