OpenglES2.0 for Android:来画个三角形吧

Android OpenGLES2.0开发(三):绘制一个三角形 本章讲解了OpenGL ES中最基本图形三角形的绘制,让我们最OpenGL ES绘制流程有一个全面的了解 阅读详情

OpenglES2.0 for Android:来画个三角形吧


先看看我们的整个流程:




理解坐标系:




左侧是Opengl默认的坐标系,右边是典型的android设备屏幕的坐标系。左侧的瘦瘦的三角形映射到android屏幕上就变成了胖胖的三角形(屏幕横向的时候),我们可以使用
camera和投影解决这个问题,具体怎么解决这里就先不累述了。这里我们只需要知道屏幕的左上角是(-1.0,1.0)横向向右为X轴正向,纵向向下为Y轴
负向,其范围都是从 -1到 +1。

定义三角形顶点:

我们在第一个android的小demo的工程里新建一个包shape,然后新建一个类 Triangle 。然后我们在该类中定义三角形的三个顶点的数据:
此时该类如下(Triangle.java):

<span style="font-size:14px;">package com.cumt.shape;

import android.content.Context;

public class Triangle {
	
	private Context context;
	// 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 2;
	// 每个顶点的坐标数  	X ,  Y 
    static float triangleCoords[] = { 0.0f,  0.5f ,   // top
                                     -0.5f, -0.5f ,   // bottom left
                                      0.5f, -0.5f };   // bottom right
    
    public Triangle(Context context){
    	this.context = context;
    }
}</span>

我们在该类中定义的float类型的数据并不能直接被opengl使用,float[ ]是属于虚拟机环境的,而Opengl作为本地系统库直接运行在硬件上,所以我们需要将float[ ] 转化为
FloatBuffer以使数据可以被opengl使用,此时该类如下(Triangle.java ):

<span style="font-size:14px;">package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.content.Context;

public class Triangle {
	
	private Context context;
	
	private static final int BYTES_PER_FLOAT = 4;
	private FloatBuffer vertexBuffer;
	
	// 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 2;
	// 每个顶点的坐标数  	X ,  Y 
    static float triangleCoords[] = { 0.0f,  0.5f ,   // top
                                     -0.5f, -0.5f ,   // bottom left
                                      0.5f, -0.5f };   // bottom right
    
    public Triangle(Context context){
    	this.context = context;
    	
    	vertexBuffer = ByteBuffer
    			.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(triangleCoords);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);
    }
}</span>

编写着色器:

着色器语言(shading language)是opengl es2.0 比opengl es 1.X 新增的内容,使我们更加自由的实现自己想要的效果,该语言基于C/C++。该语言的具体类型等等这里不详细说明。

我们需要两个着色器——顶点着色器和片段着色器,顾名思义顶点着色器是用于处理顶点坐标的(不要与屏幕的像素点混淆),而片段着色器用于处理片段,所谓片段简单理解就是顶点着色器处理后的一堆顶点形成的片段。

现在我们来编写这两个顶点着色器:在 res 目录下新建一个文件夹 raw 在该文件夹下新建一个文件simple_vertex_shader.glsl ,其内容如下:

<span style="font-size:14px;">attribute vec4 a_Position;   
  		
void main()                    
{                              
    gl_Position = a_Position;
}   </span>

gl_Position即opengl定义的顶点的坐标,我们目的就是通过这个来告诉opengl我们的顶点数据。vec4是着色器语言中的向量类型的一种,包含了四个浮点数的向量。

接下来在raw文件夹内新建文件 simple_fragment_shader.glsl ,其内容如下:

<span style="font-size:14px;">precision mediump float; 
      	 								
uniform vec4 u_Color;     							
  
void main()                    		
{                              	
    gl_FragColor = u_Color;                                  		
}</span>

这里我们只传入一个颜色信息。这里注意一下 上面顶点着色器的 限定符 attribute 和 uniform 。attribute 一般用于每个顶点各不相同的量,如顶点位置等,后者一般用于
同一组顶点组成的相同的量,如光源位置,一组颜色等。


编译着色器及绘制:

接下来我们进行最后一步,编译着色器以及绘制三角形,这里我们来写一个工具类用于加载着色器程序以及编译着色器。
首先新建一个包utils ,此时目录结构如下图所示:


在utils包下新建类 LoggerConfig 用于管理我们的日志(是否输出Logcat的信息),代码如下(LoggerConfig.java):

<span style="font-size:14px;">package com.cumt.utils;

public class LoggerConfig {
    public static final boolean ON = true;
}</span>

再在该包下新建类ShaderHelper 用于加载着色器程序以及编译着色器:

<span style="font-size:14px;">package com.cumt.utils;

import static android.opengl.GLES20.GL_COMPILE_STATUS;
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_VALIDATE_STATUS;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glDeleteProgram;
import static android.opengl.GLES20.glDeleteShader;
import static android.opengl.GLES20.glGetProgramInfoLog;
import static android.opengl.GLES20.glGetProgramiv;
import static android.opengl.GLES20.glGetShaderInfoLog;
import static android.opengl.GLES20.glGetShaderiv;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glValidateProgram;
import android.util.Log;

public class ShaderHelper {
	
    private static final String TAG = "ShaderHelper";

    /**
     * 加载并编译顶点着色器,返回得到的opengl id
     * @param shaderCode
     * @return
     */
    public static int compileVertexShader(String shaderCode) {
        return compileShader(GL_VERTEX_SHADER, shaderCode);
    }

    /**
     * 加载并编译片段着色器,返回opengl id
     * @param shaderCode
     * @return
     */
    public static int compileFragmentShader(String shaderCode) {
        return compileShader(GL_FRAGMENT_SHADER, shaderCode);
    }

    /**
     * 加载并编译着色器,返回opengl id
     * @param type
     * @param shaderCode
     * @return
     */
    private static int compileShader(int type, String shaderCode) {
        // 建立新的着色器对象
        final int shaderObjectId = glCreateShader(type);

        if (shaderObjectId == 0) {
            if (LoggerConfig.ON) {
                Log.w(TAG, "不能创建新的着色器.");
            }

            return 0;
        }

        // 传递着色器资源代码.
        glShaderSource(shaderObjectId, shaderCode);

        //编译着色器
        glCompileShader(shaderObjectId);

        // 获取编译的状态
        final int[] compileStatus = new int[1];
        glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS,
            compileStatus, 0);

        if (LoggerConfig.ON) {
        	//打印log
            Log.v(TAG, "代码编译结果:" + "\n" + shaderCode
                + "\n:" + glGetShaderInfoLog(shaderObjectId));
        }

        // 确认编译的状态
        if (compileStatus[0] == 0) {
            // 如果编译失败,则删除该对象
            glDeleteShader(shaderObjectId);

            if (LoggerConfig.ON) {
                Log.w(TAG, "编译失败!.");
            }

            return 0;
        }

        // 返回着色器的opengl id
        return shaderObjectId;
    }

    /**
     * 链接顶点着色器和片段着色器成一个program
     * 并返回这个pragram的opengl id
     * @param vertexShaderId
     * @param fragmentShaderId
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {

        // 新建一个program对象
        final int programObjectId = glCreateProgram();

        if (programObjectId == 0) {
            if (LoggerConfig.ON) {
                Log.w(TAG, "不能新建一个 program");
            }

            return 0;
        }

        // Attach the vertex shader to the program.
        glAttachShader(programObjectId, vertexShaderId);

        // Attach the fragment shader to the program.
        glAttachShader(programObjectId, fragmentShaderId);

        // 将两个着色器连接成一个program
        glLinkProgram(programObjectId);

        // 获取连接状态
        final int[] linkStatus = new int[1];
        glGetProgramiv(programObjectId, GL_LINK_STATUS,
            linkStatus, 0);

        if (LoggerConfig.ON) {
            // Print the program info log to the Android log output.
            Log.v(
                TAG,
                "Results of linking program:\n"
                    + glGetProgramInfoLog(programObjectId));
        }

        // 验证连接状态
        if (linkStatus[0] == 0) {
            // If it failed, delete the program object.
            glDeleteProgram(programObjectId);

            if (LoggerConfig.ON) {
                Log.w(TAG, "连接 program 失败!.");
            }

            return 0;
        }

        // Return the program object ID.
        return programObjectId;
    }

    /**
     * Validates an OpenGL program. Should only be called when developing the
     * application.
     */
    public static boolean validateProgram(int programObjectId) {
        glValidateProgram(programObjectId);
        final int[] validateStatus = new int[1];
        glGetProgramiv(programObjectId, GL_VALIDATE_STATUS,
            validateStatus, 0);
        Log.v(TAG, "Results of validating program: " + validateStatus[0]
            + "\nLog:" + glGetProgramInfoLog(programObjectId));

        return validateStatus[0] != 0;
    }

    /**
     * /**
     * 编译,连接 ,返回 program 的 ID
     * @param vertexShaderSource
     * @param fragmentShaderSource
     * @return
     */
    public static int buildProgram(String vertexShaderSource,
        String fragmentShaderSource) {
        int program;

        // Compile the shaders.
        int vertexShader = compileVertexShader(vertexShaderSource);
        int fragmentShader = compileFragmentShader(fragmentShaderSource);

        // Link them into a shader program.
        program = linkProgram(vertexShader, fragmentShader);

        if (LoggerConfig.ON) {
            validateProgram(program);
        }
        return program;
    }
}</span>

上面整个过程如图所示:




以后我们就可以很方便的一直使用该工具类来加载编译着色器了。只需要调用buildProgram,传入两个着色器的String文本就可以获得program的id是不是很方便。
我们似乎遗忘了什么,对的我们的两个着色器的文本内容该怎么活的呢,我们再在该目录下定义一个工具类TextResourceReader类来获得文本的内容,返回String类型
代码如下(TextResourceReader.java):

<span style="font-size:14px;">package com.cumt.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.content.Context;
import android.content.res.Resources;

public class TextResourceReader {
    /**
     * Reads in text from a resource file and returns a String containing the
     * text.
     */
    public static String readTextFileFromResource(Context context,
        int resourceId) {
        StringBuilder body = new StringBuilder();

        try {
            InputStream inputStream = 
                context.getResources().openRawResource(resourceId);
            InputStreamReader inputStreamReader = 
                new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String nextLine;

            while ((nextLine = bufferedReader.readLine()) != null) {
                body.append(nextLine);
                body.append('\n');
            }
        } catch (IOException e) {
            throw new RuntimeException(
                "Could not open resource: " + resourceId, e);
        } catch (Resources.NotFoundException nfe) {
            throw new RuntimeException("Resource not found: " + resourceId, nfe);
        }

        return body.toString();
    }
}</span>

OK ,接下来就让我们来使用者两个工具类获得program的id吧,先回到 前面定义的 Triangle 类:
我们来定义一个私有方法getProgram 并在构造函数中调用 ,此时代码如下 (Triangle.java):

<span style="font-size:14px;">package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Triangle {
	
	private Context context;
	
	private static final int BYTES_PER_FLOAT = 4;
	private FloatBuffer vertexBuffer;
	
	// 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 2;
	// 每个顶点的坐标数  						X ,  Y 
    static float triangleCoords[] = { 0.0f,  0.5f ,   // top
                                     -0.5f, -0.5f ,   // bottom left
                                      0.5f, -0.5f };   // bottom right
    
    private int program;
    
    public Triangle(Context context){
    	this.context = context;
    	
    	vertexBuffer = ByteBuffer
    			.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(triangleCoords);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);
        
        getProgram();
        
    }
    
    //获取program的id
    private void getProgram(){
    	//获取顶点着色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_vertex_shader);
    	//获取片段着色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//获取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
}</span>

至此我们已经胜利在望了,接下来思考一下,我们应该做哪些工作?显然我们需要将定义的数据传入着色器中来使用。详细的步骤见下面的代码(Triangle.java):

<span style="font-size:14px;">package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Triangle {
	
	private Context context;
	
	private static final int BYTES_PER_FLOAT = 4;
	private FloatBuffer vertexBuffer;
	
	//---------第四步:定义坐标元素的个数,这里有三个顶点
	private static final int POSITION_COMPONENT_COUNT = 3;
	
	// 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 2;
	// 每个顶点的坐标数  						X ,  Y 
    static float triangleCoords[] = { 0.0f,  0.5f ,   // top
                                     -0.5f, -0.5f ,   // bottom left
                                      0.5f, -0.5f };   // bottom right
    
    private int program;
    
    
    //------------第一步 : 定义两个标签,分别于着色器代码中的变量名相同, 
    //------------第一个是顶点着色器的变量名,第二个是片段着色器的变量名
	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	
	//------------第二步: 定义两个ID,我们就是通ID来实现数据的传递的,这个与前面
	//------------获得program的ID的含义类似的
	private int uColorLocation;
	private int aPositionLocation;
	
    public Triangle(Context context){
    	this.context = context;
    	
    	vertexBuffer = ByteBuffer
    			.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(triangleCoords);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);
        
        getProgram();
        
        //----------第三步: 获取这两个ID ,是通过前面定义的标签获得的
        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		
		//---------第五步: 传入数据
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
        
    }
    
    //获取program
    private void getProgram(){
    	//获取顶点着色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_vertex_shader);
    	//获取片段着色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//获取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
    
    //----------第七步:绘制
    public void draw(){
    	GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);		
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
    }
}</span>


最后我们只需要在我们前面定义的MyRender中使用即可,此时MyRender代码(这里在构造函数中引入了Context上下文环境,因为需要我们的Triangle对象的构造函数需要Context),
如下(MyRender.java ):

<span style="font-size:14px;">package com.cumt.render;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.cumt.shape.Triangle;

import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glViewport;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;

public class MyRender implements Renderer {
	
	private Context context;
	
	public MyRender(Context context){
		this.context = context;
	}
	
	//定义三角形对象
	Triangle triangle;
	
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		Log.w("MyRender","onSurfaceCreated");
		// TODO Auto-generated method stub
		//First:设置清空屏幕用的颜色,前三个参数对应红绿蓝,最后一个对应alpha
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		triangle = new Triangle(context);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		Log.w("MyRender","onSurfaceChanged");
		// TODO Auto-generated method stub
		//Second:设置视口尺寸,即告诉opengl可以用来渲染的surface大小
		glViewport(0,0,width,height);
	}

	public void onDrawFrame(GL10 gl) {
		Log.w("MyRender","onDrawFrame");
		// TODO Auto-generated method stub
		//Third:清空屏幕,擦除屏幕上所有的颜色,并用之前glClearColor定义的颜色填充整个屏幕
		glClear(GL_COLOR_BUFFER_BIT);
		//绘制三角形
		triangle.draw();
	}
}</span>


运行结果如下:









Android OpenGLES2.0开发(十):FBO离屏渲染 FBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO)。FBO可以让我们的渲染不渲染到屏幕上,而是渲染到离屏Buffer中。可以理解为将OpenGL ES绘制的图像内容输出到一块内存中,而我们可以对这块内存进行多次绘制。当然这块内存中的数据也可保存到本地SD卡中,也可重新绘制到屏幕上用于显示。FBO是一个容器,自身不能用于渲染,需要与一些可渲染的缓冲区绑定在一起,像纹理或者渲染缓冲区。 阅读详情

相关推荐

Android OpenGLES2.0开发(六):着色器语言GLSL

该篇文章讲解了GLSL的基本语法,需要结合实例去不断练习才能熟练的使用

xiaozhiwz的专栏 1454

Android OpenGLES2.0(二)——绘制一个三角形

选择绘制三角形作为OpenGL ES 2.0的第一个实例,是因为前文中提到的,点、线、三角形是OpenGL ES世界的图形基础。无论多么复杂的几何物体,在OpenGL ES的世界里都可以用三角形拼成。关于Android OpenGL ES 三角形的绘制,在Android官方文档中有详细的说明和步骤,本文实例也是依照官方文档步骤绘制的三角形。步骤依照官方文档中的说明,Android中利用OpenGL

偶尔记录的一些学习笔记 2万+

Learn OpenGLES:画三角形

上一贴我们简单的搭建了一个OpenGL的运行窗口,也简单的对这个窗口进行绘制。  这一贴,我们将介绍OpenGL如何将CPU中的内存数据送到GPU的内存中,Shader又是如何找到这些数据,并进行绘制的。 我们将通过绘制三角形这一简单的例子,为大家简单的介绍下OpenGL的管线流程,以及如何渲染颜色,颜色渐变动画等知识。 #介绍OpenGL的管线 Opengl 中所有的事物,都是由点来

jinghouxiang的专栏 2268

IOS OpenGLES2.0 入门03 绘制彩色三角形

接上一篇上一篇的三角形是红色, 红色的代码是固定死在代码里面的, 如果要蓝色, 白色, 或者是彩色的, 这样就行不通了那么如何绘制一个彩色的三角形呢 绘制红色的三角形的Fragment Shader的代码是这样的void main(void) { gl_FragColor = vec4(1, 0, 0, 1); }之前说了gl_FragColor是该顶点的输出颜色, 正常我们不会把代码写死

闻道有先后, 术业有专攻 1743

opengles的一些图形绘制

基于openges用c写的,基于圆锥、圆、圆环、茶壶等的一些图形绘制。

android opengles2.0画三角形

对应博客链接 http://write.blog.csdn.net/postedit/53190971

OpenGL ES2.0入门之Android篇(一)——绘制三角形

OpenGL ES简介 OpenGL ES是一个为便携式或嵌入式设备例如:移动电话、监视器等发展的3D绘图API。 在Android框架中有两个基础类用于使用OpenGL ES创建和处理图形 GLSurfaceView类是OpenGL ES绘制图形的view容器 GLSurfaceView.Renderer类是用于控制上述view容器中显示什么内容 Android使用OpenGL ES绘制三角形

lb377463323的博客 1万+

Android OpenGLES2.0绘制三角形(二)

选择绘制三角形作为OpenGL ES 2.0的第一个实例,是因为前文中提到的,点、线、三角形是OpenGL ES世界的图形基础。无论多么复杂的几何物体,在OpenGL ES的世界里都可以用三角形拼成。关于Android OpenGL ES 三角形的绘制,在Android官方文档中有详细的说明和步骤,本文实例也是依照官方文档步骤绘制的三角形。 步骤 依照官方文档中的说明,Android中利用OpenGL ES 2.0绘制三角形的步骤为: 1. 在AndroidManifest.xml文件中设置使用的OpenGL ES的版本: <!-- Tell the system this app re

Android OpenGL ES2.0学习(一)基础三角形案例

OpenGL ES 2.0与OpenGL ES 1.x的一个最大不同就是用可编程渲染管线代替了原有的固定渲染管线。在学习OpenGL ES 2.0之前,有必要先了解一下OpenGL ES 1.x渲染管线的工作原理。渲染管线有时也被称为渲染流水线,一般是由显示芯片内部的处理图形信号的并行处理单元组成。与普通应用程序通过CPU串行执行不同,将渲染工作通过渲染管线中多个相互独立的处理单元进行并行处理后,渲

SakuraMashiro的博客 821

金现代面试题—— i++,++i, &&,&|自动装箱和自动拆箱(int,byte,float等)|取int等基本类型的最大值

public static void main(String[] args){ int i=1,j=1; if(i++==2&amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp;++j==2){ i=7; } System.out.println(“i=”+i+”,j=”+j); } 请给出 输出结果:

老孟嘚吧嘚 1396

浅析图像处理中的名词:Gamma 校正(Gamma变换、Gamma调整)

浅析图像处理中的名词:Gamma 校正(Gamma变换、Gamma调整)

qq_51908382的博客 1万+

Android OpenGLES2.0开发(五):绘制正方形和圆形

本章节我们用OpenGL ES绘制了正方形和圆形,让我们对OpenGL ES绘制图形又加深的印象

xiaozhiwz的专栏 2207

基于STC8G1K08A单片机的EC11旋转编码器控制(江科大同款,代码可直接使用)

本文介绍了STC8G系列单片机与EC11旋转编码器的接口实现方案。硬件方面详细说明了EC11编码器的机械结构、电气特性及与单片机的典型连接电路,包括上拉电阻和消抖设计。软件部分重点阐述了状态检测算法,通过状态机和查找表实现方向判断,采用中断方式检测信号变化,并配合定时器进行软件消抖处理。文章还提供了完整的代码实现,包括GPIO初始化、中断服务函数、状态转换逻辑等关键模块,展示了如何通过串口输出验证旋转计数。最后讨论了抗抖动优化、高低速应用场景等实际问题解决方案。该方案在菜单导航、参数调节等场景中具有实用价值

weixin_74013772的博客 322

OpenGLES2.0-绘制一个三角形

https://blog.csdn.net/junzia/article/category/6462864

SingleTigerkin的博客 808

YOLOv11改进策略【模型轻量化】| 替换骨干网络 CVPR-2024 RepViT 轻量级的Vision Transformers架构

本文记录的是基于RepVit的YOLOv11轻量化改进方法研究。RepVit的网络结构借鉴ViT的设计理念,通过分离的token mixe和减少推理时的计算和内存成本,同时减少扩展比率并增加宽度,降低延迟,并通过来弥补参数大幅减少的问题,提高了准确性。本文在替换骨干网络中配置了原论文中的和五种模型,以满足不同的需求。

Limiiiing的博客 2975

Android OpenGLES2.0(一)OpenGLES2.0基础介绍

Andoid OpenGlES基础认识,OpenGL在不同的平台上有不同的实现,但是它定义好了专业的程序接口,不同的平台都是遵照该接口来进行实现的,思想完全相同,方法名也是一致的,所以使用时也基本一致,只需要根据不同的语言环境稍有不同而已。OpenGL这套3D图形API从1992年发布的1.0版本到目前最新2014年发布的4.5版本,在众多平台上多有着广泛的使用。...

主要记录一些问题处理记录,部分是作为知识库使用 2400

Alist/Openlist 的 WebDAV 老是连不上?别再乱试了,这才是标准答案!(1Panel如何使用Alist/Openlist作为备份账号)

😫服务器硬盘告急,想用 Alist/Openlist 的 WebDAV 备份却屡屡失败?别慌!本文带你揭秘正确的连接地址和权限配置,手把手教你搞定 1Panel 备份设置,让你的网盘秒变备份仓库!

XiaoqiangClub的博客 5582

OpenglES2.0 Android:画矩形

OpenglES2.0 for Android:来画个矩形吧 原文链接:https://blog.csdn.net/cassiePython/article/details/51553842 OK,开始动手做吧,首先在上一节的项目中的shape目录下新建一个类——Square (Square.java),然后定义矩形的四个顶点的坐标,此时代码如下(Square.java): pack...

jacke121的专栏 2895

把大表拆成更好用的“小仓库”:SAP HANA 表分区设计、分区裁剪与内存代价全解析

摘要:本文深入解析SAP HANA分区技术的核心价值与实现策略。作为内存数据库的"手术刀",分区通过将逻辑表横向切分为独立物理容器,显著提升并行计算能力与查询效率。重点探讨了三种分区类型(Range、Hash、Round-robin)的适用场景,以及分区裁剪的优化原理与前提条件。特别指出分区设计需要平衡查询模式与数据分布,并揭示了分区对内存占用的双向影响机制:既可能因字典复制增加消耗,也可能因数据纯度提升压缩率。通过电商订单表等实际案例,展示了合理分区如何将分钟级报表优化至秒级响应。文章

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1648

【AI智能体】Claude Code 高级编程技巧实战项目详解

Claude Code 高级编程技巧实战项目详解

congge 1万+

Android OpenGLES2.0开发(七):纹理贴图之显示图片

本文讲解了纹理贴图的基本用法,纹理坐标和OpenGL ES坐标系的不同

xiaozhiwz的专栏 1923

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

OpenglES2.0 for Android:来画个矩形吧

OpenglES2.0 for Android:来画个矩形吧 上一节中我们绘制了一个三角形,我们在上一节的基础上来完成矩形的绘制 。 OK,开始动手做吧,首先在上一节的项目中的shape目录下新建一个类——Square (Square.java),然后定义矩形的四个顶点的坐标,此时代码如下(Square.java): package com.cumt.shape;

cassiePython的专栏 7743

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Android OpenGLES2.0(一)——了解OpenGLES2.0

OpenGL在不同的平台上有不同的实现,但是它定义好了专业的程序接口,不同的平台都是遵照该接口来进行实现的,思想完全相同,方法名也是一致的,所以使用时也基本一致,只需要根据不同的语言环境稍有不同而已。OpenGL这套3D图形API从1992年发布的1.0版本到目前最新2014年发布的4.5版本,在众多平台上多有着广泛的使用。OpenGL ES相对于OpenGL来说,减少了许多不是必须的方法和数据类型,去掉了不必须的功能,对代价大的功能做了限制,比OpenGL更为轻量。在OpenGL ES的世界里,没有四边形

liuliuhelingdao的博客 2016

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

上一篇: OpenglES2.0 for Android:第一个OpenglES应用
下一篇: OpenglES2.0 for Android:来画个矩形吧
cassiePython
博客等级 码龄12年 303粉丝 135原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值