技术标签: AndroidStudio中配置GLSL环境 OpenGL ES AndroidStudiio中GLSL插件 OpenGL ES简单使用
OpenGL ES之一——概念扫盲
OpenGL ES之二——Android中的OpenGL ES概述
OpenGL ES之三——绘制纯色背景
OpenGL ES之四——绘制点,线,三角形
OpenGL ES之五——相机和投影,绘制等腰三角形
OpenGL ES之六——绘制矩形和圆形
OpenGL ES之七——着色器语言GLSL
OpenGL ES之八——GLES20类和Matrix类
OpenGL ES之九——相机和投影
OpenGL ES之十——纹理贴图(展示一张图片)
OpenGL ES之十一——绘制3D图形
OpenGL ES之十二——地球仪和VR图
上一篇中介绍了使用OpenGL ES的使用流程,完成了纯色背景的绘制。但是其中没有涉及具体几何形状所以并没有使用到顶点着色器和片段着色器
,这篇文章中将着重介绍
。
1.配置OpenGL ES环境:在AndroidManifest.xml文件中设置使用的OpenGL ES的版本:
<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
2.GLSurfaceView作为展示View,图形的具体渲染工作都是在Render中完成。
3.实现GLSurfaceView的Render,在Render中完成点,线,三角形的绘制,具体行为有:
所以本文重点是:了解顶点着色器和片段着色器
AndroidManifest.xml文件中设置使用的OpenGL ES的版本
<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.opengl.GLSurfaceView
android:id="@+id/simple_shape_gls"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
public class SimpleShapeActivity extends AppCompatActivity {
private GLSurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_simple_shape);
initView();
}
private void initView() {
glSurfaceView = findViewById(R.id.simple_shape_gls);
glSurfaceView.setEGLContextClientVersion(3);
GLSurfaceView.Renderer renderer = new SimpleShapeRender();
glSurfaceView.setRenderer(renderer);
}
}
顶点着色器和片段着色器
。这里先不详细的介绍GLSL语言,只是介绍一下AndroidStudio中配置GLSL环境和简单的介绍涉及到的一些api,后面会单独介绍一些重要的特性和api
。想要在Android Studio中开发GLSL需要安装相应的插件,因为默认是不支持的,所以Android Studio中不支持关键字高亮和智能提示。下面看一下如何安装插件:
如下图,进入Settings页面后找到Plugins选项,在上方输入框中搜索GLSL,从而找到相应插件。如果从未安装过,那么右侧的Update按钮处会是一个Install按钮。我们点击Install安装,最后点击ok完成即可。
我们在开发时候一般将GLSL文件放在raw文件夹或者Assets文件夹中,这里放在raw中。在raw中新建一个GLSl文件如下,和生成一个其他文件一样,名称为GLSL Shader。这里分为顶点着色器,片段着色器,内容按照GLSL规则编写。
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;
out vec4 vColor;
void main() {
gl_Position = vPosition;
gl_PointSize = 10.0;
vColor = aColor;
}
第一行:着色器的版本3.0,OpenGL ES 2.0版本可以不写。
第二行:输入一个名为vPosition的4分量向量,layout (location = 0)表示这个变量的位置是顶点属性0。
第三行:输入一个名为aColor的4分量向量,layout (location = 1)表示这个变量的位置是顶点属性1。
第四行:输出一个名为vColor的4分量向量,后面输入到片段着色器中。
第五行:main函数
第六行:gl_Position赋值为vPosition
第七行:gl_PointSize 绘制点的直径10
第八行:将输入数据aColor拷贝到vColor的变量中。
gl_Position,gl_PointSize为Shader内置变量,分别为顶点位置,点的直径。
#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fragColor;
void main() {
fragColor = vColor;
}
第一行:着色器的版本,OpenGL ES 2.0版本可以不写。
第二行:声明着色器中浮点变量的默认精度。
第三行: 声明一个输入名为vColor的4分向量,来自上面的顶点着色器。
第四行:着色器声明一个输出变量fragColor,这个是一个4分量的向量。
第五行:mian函数
第六行:表示将输入的颜色值数据拷贝到fragColor变量中,输出到颜色缓冲区。
//三个顶点的位置参数
private float triangleCoords[] = {
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
//三个顶点的颜色参数
private float color[] = {
1.0f, 0.0f, 0.0f, 1.0f,// top
0.0f, 1.0f, 0.0f, 1.0f,// bottom left
0.0f, 0.0f, 1.0f, 1.0f// bottom right
};
public SimpleShapeRender() {
//顶点位置相关
//分配本地内存空间,每个浮点型占4字节空间;将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);
//顶点颜色相关
colorBuffer = ByteBuffer.allocateDirect(color.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
colorBuffer.put(color);
colorBuffer.position(0);
}
/**
* 编译
*
* @param type 顶点着色器:GLES30.GL_VERTEX_SHADER
* 片段着色器:GLES30.GL_FRAGMENT_SHADER
* @param shaderCode 着色器语言编写的着色器程序
* @return
*/
private static int compileShader(int type, String shaderCode) {
//创建一个着色器
final int shaderId = GLES30.glCreateShader(type);
if (shaderId != 0) {
GLES30.glShaderSource(shaderId, shaderCode);
GLES30.glCompileShader(shaderId);
//检测状态
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0) {
String logInfo = GLES30.glGetShaderInfoLog(shaderId);
System.err.println(logInfo);
//创建失败
GLES30.glDeleteShader(shaderId);
return 0;
}
return shaderId;
} else {
//创建失败
return 0;
}
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//将背景设置为白色
GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
//编译顶点着色程序
String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_simple_shade);
int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
//编译片段着色程序
String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_simple_shade);
int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
//连接程序
mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
//在OpenGLES环境中使用程序
GLES30.glUseProgram(mProgram);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置绘制窗口
GLES30.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
//把颜色缓冲区设置为我们预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//准备坐标数据
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
//启用顶点位置句柄
GLES30.glEnableVertexAttribArray(0);
//准备颜色数据
GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
//启用顶点颜色句柄
GLES30.glEnableVertexAttribArray(1);
//绘制三个点
//GLES30.glDrawArrays(GLES30.GL_POINTS, 0, POSITION_COMPONENT_COUNT);
//绘制三条线
GLES30.glLineWidth(3);//设置线宽
GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, POSITION_COMPONENT_COUNT);
//绘制三角形
//GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
}
下面是涉及到的完整类
public class SimpleShapeRender implements GLSurfaceView.Renderer {
//一个Float占用4Byte
private static final int BYTES_PER_FLOAT = 4;
//三个顶点
private static final int POSITION_COMPONENT_COUNT = 3;
//顶点位置缓存
private final FloatBuffer vertexBuffer;
//顶点颜色缓存
private final FloatBuffer colorBuffer;
//渲染程序
private int mProgram;
//三个顶点的位置参数
private float triangleCoords[] = {
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
//三个顶点的颜色参数
private float color[] = {
1.0f, 0.0f, 0.0f, 1.0f,// top
0.0f, 1.0f, 0.0f, 1.0f,// bottom left
0.0f, 0.0f, 1.0f, 1.0f// bottom right
};
public SimpleShapeRender() {
//顶点位置相关
//分配本地内存空间,每个浮点型占4字节空间;将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);
//顶点颜色相关
colorBuffer = ByteBuffer.allocateDirect(color.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
colorBuffer.put(color);
colorBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//将背景设置为白色
GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
//编译顶点着色程序
String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_simple_shade);
int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
//编译片段着色程序
String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_simple_shade);
int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
//连接程序
mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
//在OpenGLES环境中使用程序
GLES30.glUseProgram(mProgram);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置绘制窗口
GLES30.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
//把颜色缓冲区设置为我们预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
//准备坐标数据
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
//启用顶点位置句柄
GLES30.glEnableVertexAttribArray(0);
//准备颜色数据
GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
//启用顶点颜色句柄
GLES30.glEnableVertexAttribArray(1);
//绘制三个点
//GLES30.glDrawArrays(GLES30.GL_POINTS, 0, POSITION_COMPONENT_COUNT);
//绘制三条线
GLES30.glLineWidth(3);//设置线宽
GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, POSITION_COMPONENT_COUNT);
//绘制三角形
//GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
}
}
public class ShaderUtils {
private static final String TAG = "ShaderUtils";
/**
* 编译顶点着色器
* @param shaderCode
*/
public static int compileVertexShader(String shaderCode) {
return compileShader(GLES30.GL_VERTEX_SHADER, shaderCode);
}
/**
* 编译片段着色器
* @param shaderCode
*/
public static int compileFragmentShader(String shaderCode) {
return compileShader(GLES30.GL_FRAGMENT_SHADER, shaderCode);
}
/**
* 编译
* @param type 顶点着色器:GLES30.GL_VERTEX_SHADER
* 片段着色器:GLES30.GL_FRAGMENT_SHADER
* @param shaderCode
*/
private static int compileShader(int type, String shaderCode) {
//创建一个着色器
final int shaderId = GLES30.glCreateShader(type);
if (shaderId != 0) {
GLES30.glShaderSource(shaderId, shaderCode);
GLES30.glCompileShader(shaderId);
//检测状态
final int[] compileStatus = new int[1];
GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
if (compileStatus[0] == 0) {
String logInfo = GLES30.glGetShaderInfoLog(shaderId);
System.err.println(logInfo);
//创建失败
GLES30.glDeleteShader(shaderId);
return 0;
}
return shaderId;
} else {
//创建失败
return 0;
}
}
/**
* 链接小程序
* @param vertexShaderId 顶点着色器
* @param fragmentShaderId 片段着色器
*/
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
//创建一个空的OpenGLES程序
final int programId = GLES30.glCreateProgram();
if (programId != 0) {
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序中
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.glLinkProgram(programId);
final int[] linkStatus = new int[1];
GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
String logInfo = GLES30.glGetProgramInfoLog(programId);
System.err.println(logInfo);
GLES30.glDeleteProgram(programId);
return 0;
}
return programId;
} else {
//创建失败
return 0;
}
}
/**
* 验证程序片段是否有效
* @param programObjectId
*/
public static boolean validProgram(int programObjectId) {
GLES30.glValidateProgram(programObjectId);
final int[] programStatus = new int[1];
GLES30.glGetProgramiv(programObjectId, GLES30.GL_VALIDATE_STATUS, programStatus, 0);
return programStatus[0] != 0;
}
}
public class ResReadUtils {
/**
* 读取资源
* @param resourceId
*/
public static String readResource(int resourceId) {
StringBuilder builder = new StringBuilder();
try {
InputStream inputStream = App.getInstance().getResources().openRawResource(resourceId);
InputStreamReader streamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(streamReader);
String textLine;
while ((textLine = bufferedReader.readLine()) != null) {
builder.append(textLine);
builder.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
return builder.toString();
}
}
着重说明一个地方
在onDrawFrame中的glVertexAttribPointer和glEnableVertexAttribArray中参数比较不好理解,下面着重说一下。
//准备坐标数据
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer);
//启用顶点位置句柄
GLES30.glEnableVertexAttribArray(0);
//准备颜色数据
GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
//启用顶点颜色句柄
GLES30.glEnableVertexAttribArray(1);
public static void glVertexAttribPointer(
int indx,
int size,
int type,
boolean normalized,
int stride,
java.nio.Buffer ptr
)
layout (location = 0) in vec4 vPosition; layout (location = 1) in vec4 aColor;
我们会发现按照顶点数据如下
//三个顶点的位置参数
private float triangleCoords[] = {
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
理应绘制成的是等腰直角三角形啊,但是现在怎么不是。这个和第一篇OpenGL ES文中提到的“使用的是虚拟坐标”有关。
同时我们的手机如果横过来的话,以三角形为例会变成如下的样子。
我们发现形状和竖屏的时候完全不同了(如果是三个点,或者是绘制的三条线也会出项相应的变化,因为三个顶点的位置在不同屏幕状态下“发生了变化”)。要想解决上面的问题就要了解坐标系,投影等知识放在下一篇中详细介绍。
文章浏览阅读2.6k次,点赞5次,收藏13次。1)5.2.1弹出两次已连接或者未连接这是因为你同时打开了流量和WiFi,他就会发出两次广播。2)5.3.1中发送自定义广播问题标准广播未能弹出消息:Intent intent=new Intent("com.example.broadcasttest.MY_BROADCAST");sendBroadcast(intent);上述已经失效了。修改:Intent intent=new Intent("com.example.broadcasttest...._代码里的广播错误
文章浏览阅读249次。作者 |平名 阿里服务端开发技术专家导读:Kubernetes 作为云原生时代的“操作系统”,熟悉和使用它是每名用户的必备技能。本篇文章概述了容器服务 Kubernet..._k8知识库
文章浏览阅读923次。分别是etc/pear.conf,etc/php-fpm.conf, etc/php-fpm.d/www.conf,lib/php.ini。php8安装基本一致,因为一个服务期内有2个版本,所以注意修改不同的安装目录和端口号。可以直接使用sbin下的nginx命令启动服务。完成编译安装需要gcc支持,如果没有,使用如下命令安装。安装过程基本一致,下面是安装7.1.33的步骤。执行如下命令,检查已经安装的包和可安装的包。执行如下命令,检查已经安装的包和可安装的包。执行如下命令,检查已经安装的包和可安装的包。_tencentos-3.1
文章浏览阅读3.1w次,点赞21次,收藏75次。import urllib.requesturl = 'https://www.python.org'# 方式一response = urllib.request.urlopen(url)print(type(response)) # <class 'http.client.HTTPResponse'># 方式二request = urllib.request.Req..._urllib.request.urlopen(url)
文章浏览阅读1.5k次,点赞12次,收藏15次。如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?
文章浏览阅读1.2k次。RS485总线常识 2010-10-12 15:56:36| 分类: 知识储备 | 标签:rs485 总线 传输 差分 |字号大中小 订阅RS485总线RS485采用平衡发送和差分接收方式实现通信:发送端将串行口的TTL电平信号转换成差分信号A,B两路输出,经过线缆传输之后在接收端将差分信号还原成TTL电平信号。由于传输线通常使用双绞线,又是差分传输,所_rs485 差分走綫間距
文章浏览阅读621次。u-boot、linux烧录_uboot制作
文章浏览阅读1.2w次,点赞10次,收藏44次。windos上git安装,git bash安装_64-bit git for windows setup.
文章浏览阅读196次。环形链表(算法java)的两种解决方法_java 实现环形链表
文章浏览阅读5.7k次。Airflow什么是 Airflow?Airflow 的架构Airflow 解决哪些问题一、docker-compose 安装airflow(postgres)1、创建启动文件airflow-docker-compose.yml.1.1、添加挂载卷,需要修改airflow-docker-compose.yml的位置2、创建本地配置文件airflow.cfg2.1、如果想修改WEB URL地址,需要修改airflow.cfg中以下两个地方3、之后up -d直接启动即可web访问地址:二、存储数据库更换post_airflow docker
文章浏览阅读28次。选题背景:随着社会的发展和教育的普及,高校教务管理系统在现代高等教育中扮演着至关重要的角色。传统的手工管理方式已经无法满足高校日益增长的规模和复杂的管理需求。因此,开发一套高效、智能的教务管理系统成为了当今高校管理的迫切需求。选题意义:高校教务管理系统的开发具有重要的意义和价值。首先,它可以提高高校教务管理的效率和准确性。通过自动化处理学生选课、排课、考试安排等繁琐的事务,大大减轻了教务人员的工作负担,提高了工作效率。同时,系统可以实时更新学生信息和课程信息,减少了数据错误和冗余,保证了管理的准确性
文章浏览阅读132次。首页>基础教程>常用类>常用 Integer类Java Integer转换double,float,int,long,stringjava中Integer类可以很方便的转换成double,float,int,long,string等类型,都有固定的方法进行转换。方法double doubleValue() //以 double 类型返回该 Integer 的值。flo..._java integet接收float类型的参数