第一部分 概念:
1) 引入
OpenGL ES 混合本质上是将 2 个片元的颜色进行调和,产生一个新的颜色。OpenGL ES 混合发生在片元通过各项测试之后,准备进入帧缓冲区的片元和原有的片元按照特定比例加权计算出最终片元的颜色值,不再是新(源)片元直接覆盖缓冲区中的(目标)片元。
2)应用
启用 OpenGL ES 混合使用 需要在绘画前启动glEnable(GL_BLEND);
。
然后通过 void glBlendFunc(GLenum sfactor, GLenum dfactor);
设置混合的方式,其中 sfactor 表示源因子,dfactor 表示目标因子。
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// GL_SRC_ALPHA 表示源因子取值为源颜色的 alpha
// GL_ONE_MINUS_SRC_ALPHA 表示目标因子取值为 1- alpha(源颜色的 alpha)
// 操作符默认为 GL_FUNC_ADD ,即加权相加。
// 混合公式变成了 源颜色向量 × alpha + 目标颜色向量 × (1- alpha)
默认的操作符是加号,我们也可以通过void glBlendEquation(GLenum mode)接口来自定义设置。
GL_FUNC_ADD:默认的,彼此元素相加
GL_FUNC_SUBTRACT:彼此元素相减
GL_FUNC_REVERSE_SUBTRACT:彼此元素相减,但顺序相反
GL_MIN:混合结果的 4 个通道值分别取 2 元素中 4 个通道较小的值
GL_MAX:混合结果的 4 个通道值分别取 2 元素中 4 个通道较大的值
glBlendFuncSeperate为 RGB 和 alpha 通道各自设置不同的混合因子
//对 RGB 和 Alpha 分别设置 BLEND 函数
void glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLenum dstAlpha);
glBlendFuncSeperate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ZERO);
上面就表示
混合结果颜色 RGB 向量 = 源颜色 RGB 向量 × alpha + 目标颜色 RGB 向量 × (1- alpha);
混合结果颜色 alpha = 源颜色 alpha × 1 + 目标颜色 alpha × 0;
这个也是默认为+号,但是可以通过glBlendEquationSeparate为 RGB 和 alpha 通道各自设置不同操作符。void glBlendEquationSeparate(GLenum modeRGB,GLenum modeAlpha);
另外需要格外注意的是,开启混合和深度测试绘制透明物体时,需要遵循物体距观察者(Camera)的距离,由远到近开始绘制,这样可以避免由于深度测试开启后(在透明物体后面)丢弃片元造成的奇怪现象。
第二部分 实践:
1)java部分
java部分在绘制部分都是跟之前一致的,只是因为混合使用到了图片的RGBA中A的维度,需要将32位的png图片在java端转换为具体的RGBA数据传递到JNI去。
NativeImpl.java
public class NativeImpl {
。。。。
//多出这个native接口
public static native void native_SetImageDataWithIndex(int index, int width,int height, byte[] byteArray);
}
MyGLSurfaceView.java
public class MyGLSurfaceView extends GLSurfaceView implements ScaleGestureDetector.OnScaleGestureListener{
public MyGLSurfaceView(Context context) {
...
//开始的时候就把内容主动传递到
loadRGBAImage(R.drawable.board_texture,0);
loadRGBAImage(R.drawable.floor,1);
loadRGBAImage(R.drawable.window,2);
}
private Bitmap loadRGBAImage(int resId, int index) {
InputStream is = this.getResources().openRawResource(resId);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
if (bitmap != null) {
int bytes = bitmap.getByteCount();
ByteBuffer buf = ByteBuffer.allocate(bytes);
bitmap.copyPixelsToBuffer(buf);
byte[] byteArray = buf.array();
renderer.setImageDataWithIndex(index, bitmap.getWidth(), bitmap.getHeight(), byteArray);
}
}
finally
{
try
{
is.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return bitmap;
}
...
private class MyRenderer implements Renderer {
...
public void setImageDataWithIndex(int index, int width, int height, byte[] bytes) {
NativeImpl.native_SetImageDataWithIndex(index, width, height, bytes);
}
}
}
2)C++部分
NativeGL.cpp
多出
std::vector<struct ImageTyep> vcImage;
/*
* Class: com_example_nineblending_native_SetImageDataWithIndex
* Method: native_SetImageDataWithIndex
* Signature: (III[B)V
*/
JNIEXPORT void JNICALL native_SetImageDataWithIndex(JNIEnv *env, jobject instance, jint index, jint width, jint height, jbyteArray byteArray)
{
struct ImageTyep itype;
int len = env->GetArrayLength (byteArray);
uint8_t* buf = new uint8_t[len];
env->GetByteArrayRegion(byteArray, 0, len, reinterpret_cast<jbyte*>(buf));
itype.data = buf;
itype.width =width;
itype.height = height;
vcImage.push_back(itype);
LOGD("native_SetImageDataWithIndex width=%d height = height", width, height);
//env->DeleteLocalRef(byteArray);
}
Blending.h
//
// Created by CreatWall_zhouwen on 2023/5/15.
//
#ifndef NINEBLENDING_BLENDING_H
#define NINEBLENDING_BLENDING_H
#include <GLES3/gl3.h>
#include <detail/type_mat.hpp>
#include <detail/type_mat4x4.hpp>
#include <vector>
#include <map>
#include "Const.h"
#define MATH_PI 3.1415926535897932384626433832802
#define RENDER_IMG_NUM 3
class Blending {
public:
Blending();
~Blending();
void CreateProgram(const char *ver, const char *frag);
void Draw();
void getTexturedata(std::vector<struct ImageTyep> vcImagetemp);
static Blending* GetInstance();
static void DestroyInstance();
void UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY);
void UpdateMatrix(glm::mat4 &mvpMatrix, int angleXRotate, int angleYRotate, float scale, glm::vec3 transVec3, float ratio);
void OnSurfaceChanged(int width, int height);
private:
GLuint program;
GLuint vertexShaderHandle;
GLuint fragShaderHandle;
GLuint m_TextureIds[RENDER_IMG_NUM];
GLint m_SamplerLoc;
GLint m_MVPMatLoc;
GLuint m_VaoIds[3];
GLuint m_VboIds[3];
std::vector<struct ImageTyep> m_vcImage;
glm::mat4 m_MVPMatrix;
std::vector<glm::vec3> windowsTrans;
std::map<GLfloat, glm::vec3> sorted;
int m_AngleX;
int m_AngleY;
int Srceenwidth;
int Srceenheight;
};
#endif //NINEBLENDING_BLENDING_H
Blending.cpp
//
// Created by CreatWall_zhouwen on 2023/5/15.
//
#include "Blending.h"
#include "Util.h"
#include "GLUtil.h"
#include <gtc/matrix_transform.hpp>
Blending* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"
GLfloat boxVertices[] = {
//position //texture coord
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
};
GLfloat flatVertices[] = {
5.0f, -0.5f, 5.0f, 2.0f, 0.0f,
-5.0f, -0.5f, 5.0f, 0.0f, 0.0f,
-5.0f, -0.5f, -5.0f, 0.0f, 2.0f,
5.0f, -0.5f, 5.0f, 2.0f, 0.0f,
-5.0f, -0.5f, -5.0f, 0.0f, 2.0f,
5.0f, -0.5f, -5.0f, 2.0f, 2.0f
};
GLfloat windowVertices[] = {
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f, 1.0f,
1.0f, -0.5f, 0.0f, 1.0f, 1.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
1.0f, -0.5f, 0.0f, 1.0f, 1.0f,
1.0f, 0.5f, 0.0f, 1.0f, 0.0f
};
Blending::Blending() {
m_SamplerLoc = GL_NONE;
m_MVPMatLoc = GL_NONE;
m_AngleX = 0;
m_AngleY = 0;
Srceenwidth = 1080;
Srceenheight = 2115;
}
Blending::~Blending() {
}
void Blending::CreateProgram(const char *ver, const char *frag) {
LOGD("CreateProgram Enter");
// 编译链接用于离屏渲染的着色器程序
program = CreateGLProgram(ver, frag, vertexShaderHandle, fragShaderHandle);
if (program == GL_NONE)
{
LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
return;
}
LOGD("CreateGLProgram Success");
m_SamplerLoc = glGetUniformLocation(program, "s_TextureMap");
m_MVPMatLoc = glGetUniformLocation(program, "u_MVPMatrix");
//创建VBO
glGenBuffers(3, m_VboIds);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(boxVertices), boxVertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(flatVertices), flatVertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(windowVertices), windowVertices, GL_STATIC_DRAW);
//创建VAO并绑定VBO
glGenVertexArrays(3, m_VaoIds);
glBindVertexArray(m_VaoIds[0]);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) (3* sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
glBindVertexArray(GL_NONE);
glBindVertexArray(m_VaoIds[1]);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) (3* sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
glBindVertexArray(GL_NONE);
glBindVertexArray(m_VaoIds[2]);
glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[2]);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (const void *) (3* sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
glBindVertexArray(GL_NONE);
//创建纹理并绑定数据
glGenTextures(RENDER_IMG_NUM, m_TextureIds);
for (int i = 0; i < RENDER_IMG_NUM; ++i)
{
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, m_TextureIds[i]);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_vcImage[i].width, m_vcImage[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_vcImage[i].data);
LOGD("width = %d, height = %d",m_vcImage[i].width, m_vcImage[i].height);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
}
//布局参数
windowsTrans.push_back(glm::vec3(-1.5f, 0.0f, -0.48f));
windowsTrans.push_back(glm::vec3( 1.5f, 0.0f, 0.51f));
windowsTrans.push_back(glm::vec3( 0.0f, 0.0f, 0.7f));
windowsTrans.push_back(glm::vec3(-0.3f, 0.0f, -2.3f));
windowsTrans.push_back(glm::vec3( 0.5f, 0.0f, -0.6f));
for (GLuint i = 0; i < windowsTrans.size(); i++)
{
GLfloat distance = std::sqrt(std::pow(windowsTrans[i].x - 0.5f, 2.0f) + std::pow(windowsTrans[i].y - 1.0f, 2.0f) + std::pow(windowsTrans[i].z - 3.0f, 2.0f));
sorted[distance] = windowsTrans[i];
}
}
void Blending::Draw() {
float ratio = (float)Srceenwidth / Srceenheight;
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);//开启深度测试
glEnable(GL_BLEND);//开启混合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//源颜色向量 × alpha + 目标颜色向量 × (1- alpha)
glUseProgram(program);
glBindVertexArray(m_VaoIds[0]);
// Bind the RGBA map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureIds[0]);
glUniform1i(m_SamplerLoc, 0);
UpdateMatrix(m_MVPMatrix, 0, 0, 1.0, glm::vec3(-1.0f, 0.0f, -1.0f), ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 36);
UpdateMatrix(m_MVPMatrix, 0, 0, 1.0, glm::vec3(2.0f, 0.0f, 0.0f), ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glBindVertexArray(m_VaoIds[1]);
// Bind the RGBA map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureIds[1]);
glUniform1i(m_SamplerLoc, 0);
UpdateMatrix(m_MVPMatrix, 0, 0, 1.0, glm::vec3(0.0f, 0.0f, 0.0f), ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glBindVertexArray(m_VaoIds[2]);
// Bind the RGBA map
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_TextureIds[2]);
glUniform1i(m_SamplerLoc, 0);
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it)
{
UpdateMatrix(m_MVPMatrix, 0, 0 , 1.0, it->second, ratio);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
glBindVertexArray(0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
}
void Blending::getTexturedata(std::vector<struct ImageTyep> vcImagetemp) {
m_vcImage = vcImagetemp;
}
Blending *Blending::GetInstance() {
if (m_pContext == nullptr)
{
m_pContext = new Blending();
}
return m_pContext;
}
void Blending::DestroyInstance() {
if (m_pContext)
{
delete m_pContext;
m_pContext = nullptr;
}
}
void Blending::UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY) {
m_AngleX = static_cast<int>(rotateX);
m_AngleY = static_cast<int>(rotateY);
}
void Blending::UpdateMatrix(glm::mat4 &mvpMatrix, int angleXRotate, int angleYRotate, float scale,
glm::vec3 transVec3, float ratio) {
LOGD("BlendingSample::UpdateMatrix angleX = %d, angleY = %d, ratio = %f", angleXRotate,
angleYRotate, ratio);
angleXRotate = angleXRotate % 360;
angleYRotate = angleYRotate % 360;
//转化为弧度角
float radiansX = static_cast<float>(MATH_PI / 180.0f * angleXRotate);
float radiansY = static_cast<float>(MATH_PI / 180.0f * angleYRotate);
// Projection matrix
//glm::mat4 Projection = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f);
//glm::mat4 Projection = glm::frustum(-ratio, ratio, -1.0f, 1.0f, 4.0f, 100.0f);
glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f);
// View matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0.5, 1, 3), // Camera is at (0,0,1), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix
glm::mat4 Model = glm::mat4(1.0f);
Model = glm::scale(Model, glm::vec3(scale, scale, scale));
Model = glm::rotate(Model, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
Model = glm::rotate(Model, radiansY, glm::vec3(0.0f, 1.0f, 0.0f));
Model = glm::translate(Model, transVec3);
mvpMatrix = Projection * View * Model;
}
void Blending::OnSurfaceChanged(int width, int height) {
Srceenwidth = width;
Srceenheight = height;
LOGD("OnSurfaceChanged Srceenwidth = %d, Srceenheight = %d, ratio = %f", Srceenwidth,Srceenheight);
}