补一张效果图:
在手机里显示线很清楚的,可能是屏幕录制帧数太低了,显示出来都看不清,实际是没问题的,有机会用另一个手机拍屏幕传一个吧,2333.
宽度加粗了4倍,这下应该能看清楚了hhhh。
由于项目功能需要,要做一个能自定义画直线的功能,网上找了许多文章或项目demo,没找到画直线的,反而是更进一步的实现类似“绘画板”功能的代码和教程较多;但是,这样的功能下,假如使用者想画一个规则的图形——比如直线,那么必须手不能抖笔直的画出来才行,应用到我这个项目里的话实在太反人类了,很不合适,于是就只好自己做了。
由于本人实力有限,也是刚接触opengles这玩意,磨磨蹭蹭拼拼凑凑了好几天总算是勉强实现了画直线的功能。
其实,做着做着就感觉这东西和android开发基本没啥关系了,无非就是android给他做了界面与交互而已,不过后续也要学习Opengl的也算是开个头吧,唠叨有点多了,下面是正文。
按步骤来说,想要实现根据手指运动轨迹画直线主要有以下几个要点:
如何绘制一条直线参考官方文档里的三角形案例可以轻松实现,有现成案例,很多博主也是根据这些内容写的教程:https://developer.android.com/training/graphics/opengl/environment
本文是也是基于官方文档的三角形绘制案例改写的,具体如何绘制固定直线。
由于我没有中途保存过绘制直线的代码,下面提供的是已经实现自定义画线功能后的代码了,建议自己实现绘制一条固定坐标直线后再看后面的内容。
仅仅实现这个功能其实也很简单,在renderer或者glsurfaceView里设置一个触摸事件,动态的更新直线类中的起点与终点的坐标即可。相关事件参考代码如下:(重点是事件内容)
Activity中设置时间的oncreate函数如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
glSurfaceView = new OneGlSurfaceView(this);
mRenderer = new OneGlRenderer();
glSurfaceView.setRenderer(mRenderer);
setContentView(glSurfaceView);
glSurfaceView.setOnTouchListener(this);//给view监听触摸事件
}
之后是重写的Touch事件
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
/**
* 点击的开始位置
*/
case MotionEvent.ACTION_DOWN:
float[] out = mRenderer.handleTouch(event.getX(),event.getY(),mRenderer.straightLine);
currentLines = new StraightLine(new float[] {
0, 0, 0});
currentLines.setX_start(out[0]);//设置起点x坐标
currentLines.setY_start(out[1]);//设置直线起点y坐标 取按下的坐标为起点
break;
/**
* 触屏实时位置
*/
case MotionEvent.ACTION_MOVE:
//out数组保存坐标转换后的xyz坐标
out = mRenderer.handleTouch(event.getX(),event.getY(),mRenderer.straightLine);
Log.i("setPointer", String.format("x: %f, y: %f", event.getX(), event.getY()));
Log.i("setPointer", String.format("x-start: %f, y-start: %f, z-start:%f", mRenderer.straightLine.getX_start(), mRenderer.straightLine.getY_start(),mRenderer.straightLine.getZ_start()));
Log.i("setPointer", String.format("x-end: %f, y-end: %f, z-end:%f", out[0], out[1],out[2]));
Log.i("setPointer", String.format("POS: %d", mRenderer.straightLine.pointBufferPos));
currentLines.setX_end(out[0]);//设置直线终点x坐标
currentLines.setY_end(out[1]);//设置直线终点y坐标 随着移动不断更新
// out[2]中存着z坐标,但是平面用不上,所以z坐标不变,依旧是固定值0
break;
/**
* 离开屏幕的位置
*/
case MotionEvent.ACTION_UP:
break;
default:
break;
}
/**
* 注意返回值
* true:view继续响应Touch操作;
* false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置
*/
return true;
}
简单解释一下,首先众所周知,Renderer有如下三个默认存在的方法:
onSurfaceCreated() - 调用一次以设置视图的 OpenGL ES 环境。
onDrawFrame() - 每次重新绘制视图时调用。
onSurfaceChanged() - 当视图的几何图形发生变化(例如当设备的屏幕方向发生变化)时调用。
于是,由于每次我们触摸移动,更新坐标以后,图形就发生了变化,需要重新绘制图形,所以会重新调用renderer类当中的onSurfaceChanged()和onDrawFrame()
一般的直线类的绘制函数draw()会放在**onDrawFrame()**里。
opengl内部的坐标范围是(-1,1),而我们通过触摸事件获取到的是屏幕坐标,想要直接把屏幕坐标作为点的坐标传值肯定是不行的。
有一点需要特别注意的是,GLES20中对于Matrix.frustumM的左右边缘上下界一定要设置为(-1,1)否则会出现坐标转换的分布无法覆盖整个屏幕,从而出现坐标转换的差异。(有不少教程是直选用了部分象限,因而左右的上下界设置为了Ratio的值)
上述代码中,我用了一个out的数组存储坐标转换后的坐标,转换函数我写在了我的Renderer类里,主要是调用了GLU库里的 GLU.gluUnProject()函数,在对得到的坐标,用变换矩阵相乘,由于笔者还没学习图形学,所以具体原理并不是很懂,但是网上有不少教程有对此做说明。
(PS:虽然我看了以后还是一知半解,估计等我学了图形学后就明白了,大概)。
public float[] handleTouch(float rx, float ry,StraightLine line) {
float[] near_xyz = line.unProject(rx, ry, 0, mVMatrix, mProjMatrix, viewWidth, viewHeight);
return near_xyz;
}
UnProject()函数如下.
public float[] unProject(float xTouch, float yTouch, float winz,
float[] viewMatrix,
float[] projMatrix,
int width, int height) {
int[] viewport = {
0, 0, width, height};
float[] out = new float[3];
float[] temp = new float[4];
float[] temp2 = new float[4];
// get the near and far ords for the click
float winx = xTouch, winy = (float) viewport[3] - yTouch;
int result = GLU.gluUnProject(winx, winy, winz, viewMatrix, 0, projMatrix, 0, viewport, 0, temp, 0);
Matrix.multiplyMV(temp2, 0, viewMatrix, 0, temp, 0);
if (result == 1) {
out[0] = temp2[0] / temp2[3];
out[1] = temp2[1] / temp2[3];
out[2] = temp2[2] / temp2[3];
}
return out;
}
简单说明一下原理,定义一个直线类的ArrayList,不断的存储直线,每次遍历ArrayList将里面所有的直线类都绘制一遍,没想到吧,你以为是你在原先的画面上一条一条线的加上去的,实际上是每次所有的线都给你重新绘制了一边哒!!!
由于计算机处理速度很快,就给了你一种是一条线一条线加进去的错觉,hhh。
下面放代码,有两种,一种写在Activity里的:
public class StraightLineActivity extends AppCompatActivity implements View.OnTouchListener {
private OneGlSurfaceView glSurfaceView;
private OneGlRenderer mRenderer;
public StraightLine currentLines = null; //当前绘制的线
public List<StraightLine> linesList = new ArrayList<>(); //当前绘制线的表
public long frameCount = 0; //共绘制了多少帧
private float ratio;
private int viewWidth;
private int viewHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
glSurfaceView = new OneGlSurfaceView(this);
mRenderer = new OneGlRenderer();
glSurfaceView.setRenderer(mRenderer);
setContentView(glSurfaceView);
//glSurfaceView.setOnTouchListener(this);
}
@Override
protected void onPause() {
super.onPause();
if (glSurfaceView != null) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (glSurfaceView != null) {
glSurfaceView.onResume();
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
/**
* 点击的开始位置
*/
case MotionEvent.ACTION_DOWN:
float[] out = mRenderer.handleTouch(event.getX(),event.getY(),mRenderer.straightLine);
currentLines = new StraightLine(new float[] {
0, 0, 0});
currentLines.setX_start(out[0]);
currentLines.setY_start(out[1]);
synchronized (linesList) {
linesList.add(currentLines);
}
break;
/**
* 触屏实时位置
*/
case MotionEvent.ACTION_MOVE:
out = mRenderer.handleTouch(event.getX(),event.getY(),mRenderer.straightLine);
Log.i("setPointer", String.format("x: %f, y: %f", event.getX(), event.getY()));
Log.i("setPointer", String.format("x-start: %f, y-start: %f, z-start:%f", mRenderer.straightLine.getX_start(), mRenderer.straightLine.getY_start(),mRenderer.straightLine.getZ_start()));
Log.i("setPointer", String.format("x-end: %f, y-end: %f, z-end:%f", out[0], out[1],out[2]));
Log.i("setPointer", String.format("POS: %d", mRenderer.straightLine.pointBufferPos));
currentLines.setX_end(out[0]);
currentLines.setY_end(out[1]);
currentLines.draw(mRenderer.mProjMatrix,mRenderer.mVMatrix);
break;
/**
* 离开屏幕的位置
*/
case MotionEvent.ACTION_UP:
synchronized (linesList) {
linesList.add(currentLines);
}
break;
default:
break;
}
/**
* 注意返回值
* true:view继续响应Touch操作;
* false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置
*/
return true;
}
}
另一种,写render里的这种方法得现在SurfaceView里重写onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event) {
renderer.setPointer(event);
return true;
}
然后,renderer里写**setPointer()**方法
public void setPointer(MotionEvent event) {
this.x = event.getX();
this.y = event.getY();
this.z = 0f;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
currentLines = new GLLine();
synchronized (linesList) {
linesList.add(currentLines);
}
break;
case MotionEvent.ACTION_MOVE:
Log.i("setPointer", String.format("x: %f, y: %f", x, y,z));
this.x = x / height * 4f;
this.y = -y / height * 4f;
this.z = 0;
currentLines.drawLine(x, y,z);
break;
case MotionEvent.ACTION_UP:
break;
}
}
以上两种方法都可以。
到这里位置,理论上来说肯定是可以实现画多条线了,但是笔者当时到这一步以后发现还是只能绘制一条直线,排查了半天终于发现了问题所在。
由于我是参考的官方文档的draw()方法,里面有一句:
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
没注释掉这句的话,每次绘制他都会吧之前的清空掉,说是清空,其实就是全变成透明色了,前面的线重新绘制的时候就没了,每次就只剩下最后一次绘制的线了。
public class StraightLineActivity extends AppCompatActivity implements View.OnTouchListener {
private OneGlSurfaceView glSurfaceView;
private OneGlRenderer mRenderer;
public StraightLine currentLines = null; //当前绘制的线
public List<StraightLine> linesList = new ArrayList<>(); //当前绘制线的表
public long frameCount = 0; //共绘制了多少帧
private float ratio;
private int viewWidth;
private int viewHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
glSurfaceView = new OneGlSurfaceView(this);
mRenderer = new OneGlRenderer();
glSurfaceView.setRenderer(mRenderer);
setContentView(glSurfaceView);
//glSurfaceView.setOnTouchListener(this);
}
@Override
protected void onPause() {
super.onPause();
if (glSurfaceView != null) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (glSurfaceView != null) {
glSurfaceView.onResume();
}
}
}
由于本身也是参考了很多网上开源的资源做出来的,所以会各种相似,希望大家不要介意。
(1)在XML文件里面申明Opengles的使用
<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
(2)GLSurfaceView
这个很简单,我的理解是基本就是给图形一个显示的空间,本身并不复杂,无需过多关注。
public class OneGlSurfaceView extends GLSurfaceView {
private OneGlRenderer mRenderer;
public OneGlSurfaceView(Context context) {
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
// Set the Renderer for drawing on the GLSurfaceView
//setRenderer((Renderer) mRenderer);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mRenderer.setPointer(event);
return true;
}
@Override
public void setRenderer(Renderer renderer) {
super.setRenderer(renderer);
this.mRenderer = (OneGlRenderer) renderer;
}
}
(3)Renderer类
public class OneGlRenderer implements Renderer, com.example.ty.openglndk.GLSurfaceView.Renderer {
int viewWidth, viewHeight;
float[] mVMatrix = new float[16];
float[] mProjMatrix = new float[16];
private int mwidth,mheight;
float ratio;
StraightLine straightLine;
private StraightLine currentLines = null; //当前绘制的线
private List<StraightLine> linesList = new ArrayList<>(); //当前绘制线的表
@Override
public void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
@Override
public void onDrawFrame(GL10 gl) {
// Redraw background color
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 清除屏幕和深度缓存
//gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); //不加这个可以产生残影(模拟器可以)
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
// 允许设置顶点
//GL10.GL_VERTEX_ARRAY顶点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 允许设置颜色
//GL10.GL_COLOR_ARRAY颜色数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
//反走样
gl.glEnable(GL10.GL_BLEND);
//线条抗锯齿
gl.glEnable(GL10.GL_LINE_SMOOTH);
/***************绘制模型**************/
//StraightLine straightLine = new StraightLine();
currentLines = new StraightLine(new float[] {
0, 0, 0});
currentLines.draw(mProjMatrix,mVMatrix);
drawModel(gl);
/************************************/
// 取消颜色设置
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
// 取消顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
//绘制结束
gl.glFinish();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
viewWidth = width;
viewHeight = height;
// 设置透明色为清屏
//gl.glClearColor(0, 0, 0, 0);
mwidth = width;
mheight = height;
ratio = (float) width / height;
// 设置OpenGL场景的大小,(0,0)表示窗口内部视口的左下角,(w,h)指定了视口的大小
gl.glViewport(0, 0, width, height);
// 设置投影矩阵
gl.glMatrixMode(GL10.GL_PROJECTION);
// 重置投影矩阵
gl.glLoadIdentity();
// 设置视口的大小
gl.glFrustumf(-1, 1, -1, 1, 1, 10);
//以下两句声明,以后所有的变换都是针对模型(即我们绘制的图形)
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
Matrix.frustumM(mProjMatrix, 0, -1, 1, -1, 1, 3, 10);
Matrix.setLookAtM(mVMatrix,0, 2,2,9, 0f,0f,0f, 0f,1.0f,0.0f);
}
public static int loadShader(int type, String shaderCode){
// 创造顶点着色器类型(GLES20.GL_VERTEX_SHADER)
// 或者是片段着色器类型 (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// 添加上面编写的着色器代码并编译它
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public float[] handleTouch(float rx, float ry,StraightLine line) {
float[] near_xyz = line.unProject(rx, ry, 0, mVMatrix, mProjMatrix, viewWidth, viewHeight);
float[] far_xyz = line.unProject(rx, ry, 1, mVMatrix, mProjMatrix, viewWidth, viewHeight);
return near_xyz;
}
/**帧绘制**/
public void drawModel(GL10 gl) {
int count = 0;
synchronized (linesList) {
for(StraightLine line : linesList) {
count++;
Log.i("drawModel", String.format("调用次数: %d",count));
line.draw(mProjMatrix,mVMatrix);
}
}
}
public void setPointer(MotionEvent event) {
switch (event.getAction()) {
/**
* 点击的开始位置
*/
case MotionEvent.ACTION_DOWN:
float[] out = handleTouch(event.getX(),event.getY(),currentLines);
currentLines = new StraightLine(new float[] {
0, 0, 0});
currentLines.setX_start(out[0]);
currentLines.setY_start(out[1]);
//mRenderer.straightLine.x_start=out[0];
//mRenderer.straightLine.y_start=out[1];
//mRenderer.straightLine.touch(out[0],out[1],0);
//mRenderer.straightLine.z_start=out[2];
//tvTouchShowStart.setText("起始位置:(" + event.getX() + "," + event.getY());
break;
/**
* 触屏实时位置
*/
case MotionEvent.ACTION_MOVE:
//tvTouchShow.setText("实时位置:(" + event.getX() + "," + event.getY());
out = handleTouch(event.getX(),event.getY(),currentLines);
Log.i("setPointer", String.format("x: %f, y: %f", event.getX(), event.getY()));
Log.i("setPointer", String.format("x-start: %f, y-start: %f, z-start:%f", currentLines.getX_start(), currentLines.getY_start(),currentLines.getZ_start()));
Log.i("setPointer", String.format("x-end: %f, y-end: %f, z-end:%f", out[0], out[1],out[2]));
Log.i("setPointer", String.format("POS: %d", currentLines.pointBufferPos));
/*int pos = mRenderer.straightLine.pointBufferPos;
if(mRenderer.straightLine.pointBufferPos/3%2!=0){//假如此时顶点数组内的点坐标数量不为偶数
mRenderer.straightLine.touch(out[0],out[1],0,pos);
mRenderer.straightLine.vertexCount++;
}*/
currentLines.setX_end(out[0]);
currentLines.setY_end(out[1]);
//currentLines.draw(mProjMatrix,mVMatrix);
//mRenderer.straightLine.x_end=out[0];
//mRenderer.straightLine.y_end=out[1];
//mRenderer.straightLine.touch(out[0],out[1],0,pos);
//mRenderer.straightLine.z_end=out[2];
break;
/**
* 离开屏幕的位置
*/
case MotionEvent.ACTION_UP:
//tvTouchShow.setText("结束位置:(" + event.getX() + "," + event.getY());
//straightLine.y_end=event.getY()/1000;
out = handleTouch(event.getX(),event.getY(),currentLines);
//mRenderer.straightLine.x_end=out[0];
//mRenderer.straightLine.y_end=out[1];
//mRenderer.straightLine.z_end=out[2];
//mRenderer.straightLine.touch(out[0],out[1],0);
synchronized (linesList) {
linesList.add(currentLines);
}
currentLines.setX_end(out[0]);
currentLines.setY_end(out[1]);
Log.i("setPointer", String.format("List中直线个数:%d",linesList.size()));
Log.i("setPointer", String.format("List中直线1:x1:%f y1:%f z1:%f",linesList.get(0).getX_start(),linesList.get(0).getY_start(),linesList.get(0).getZ_start()));
Log.i("setPointer", String.format("List中直线1:x2:%f y2:%f z2:%f",linesList.get(0).getX_end(),linesList.get(0).getY_end(),linesList.get(0).getZ_end()));
break;
default:
break;
}
/**
* 注意返回值
* true:view继续响应Touch操作;
* false:view不再响应Touch操作,故此处若为false,只能显示起始位置,不能显示实时位置和结束位置
*/
}
}
(4)StraightLine 直线类
opengles中直线的绘制需要起始两个点的坐标,并且点的坐标都是3维的,比较关键或者看不懂的内容我都在注释里标注了。
官方文档中只提供了画三角形的实例,但是画三角形的原理与画直线是一样的,所以就根据三角形的案例直接修改了,下面是代码。
public class StraightLine {
//顶点着色程序 - 用于渲染形状的顶点的 OpenGL ES 图形代码,官网里直接有提供现成的。
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
//片段着色程序 - 用于使用颜色或纹理渲染形状面的 OpenGL ES 代码,同上官网提供。
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private FloatBuffer vertexBuffer;//节点缓冲区
private boolean updateVertex = false;
public static float x_start=0.0f;//初始起点坐标
public static float y_start=0.5f;
public static float z_start=0.0f;
public static float x_end=0.5f;//初始终点坐标
public static float y_end=-0.5f;
public static float z_end=0.0f;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;//每3个节点一个坐标
float triangleCoords[] = {
// in counterclockwise order:
x_start, y_start, z_start, // t
x_end, y_end, z_end
};
int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float[] mMVPMatrix = new float[16];//投影矩阵
float[] mMMatrix = new float[16];//变换矩阵,用来做乘法的
private float[] mMVMatrix = new float[16];//视角矩阵
private float[] position;//位置
public StraightLine(float[] position) {
this.position = position;
// 初始化ByteBuffer,长度为arr数组的长度*4,因为一个float占4个字节
ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
// 数组排列用nativeOrder
bb.order(ByteOrder.nativeOrder());
// 从ByteBuffer创建一个浮点缓冲区
vertexBuffer = bb.asFloatBuffer();
// 将坐标添加到FloatBuffer
vertexBuffer.put(triangleCoords);
// 设置缓冲区来读取第一个坐标
vertexBuffer.position(0);
pointBufferPos = 0;//记录当前数组位置
int vertexShader = OneGlRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = OneGlRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// 创建空的OpenGL ES程序
mProgram = GLES20.glCreateProgram();
// 添加顶点着色器到程序中
GLES20.glAttachShader(mProgram, vertexShader);
// 添加片段着色器到程序中
GLES20.glAttachShader(mProgram, fragmentShader);
// 创建OpenGL ES程序可执行文件
GLES20.glLinkProgram(mProgram);
}
public void draw(float[] projMatrix, float[] viewMatrix) {
//GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 将程序添加到OpenGL ES环境
GLES20.glUseProgram(mProgram);
// 获取顶点着色器的位置的句柄
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 启用三角形顶点位置的句柄
GLES20.glEnableVertexAttribArray(mPositionHandle);
//准备三角形坐标数据
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// 获取片段着色器的颜色的句柄
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
/**实现屏幕坐标向坐标转换的核心代码**/
Matrix.setIdentityM(mMMatrix, 0);
Matrix.translateM(mMMatrix, 0, position[0], position[1], position[2]);
Matrix.multiplyMM(mMVMatrix, 0, viewMatrix, 0, mMMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, projMatrix, 0, mMVMatrix, 0);
// 设置绘制三角形的颜色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// 绘制三角形
GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, vertexCount);
// 禁用顶点数组
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public float[] unProject(float xTouch, float yTouch, float winz,
float[] viewMatrix,
float[] projMatrix,
int width, int height) {
int[] viewport = {
0, 0, width, height};
float[] out = new float[3];
float[] temp = new float[4];
float[] temp2 = new float[4];
// get the near and far ords for the click
float winx = xTouch, winy = (float) viewport[3] - yTouch;
int result = GLU.gluUnProject(winx, winy, winz, viewMatrix, 0, projMatrix, 0, viewport, 0, temp, 0);
Matrix.multiplyMV(temp2, 0, viewMatrix, 0, temp, 0);
if (result == 1) {
out[0] = temp2[0] / temp2[3];
out[1] = temp2[1] / temp2[3];
out[2] = temp2[2] / temp2[3];
}
return out;
}
}
(5)Activity
public class StraightLineActivity extends AppCompatActivity implements View.OnTouchListener {
private OneGlSurfaceView glSurfaceView;
private OneGlRenderer mRenderer;
public StraightLine currentLines = null; //当前绘制的线
public List<StraightLine> linesList = new ArrayList<>(); //当前绘制线的表
public long frameCount = 0; //共绘制了多少帧
private float ratio;
private int viewWidth;
private int viewHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
glSurfaceView = new OneGlSurfaceView(this);
mRenderer = new OneGlRenderer();
glSurfaceView.setRenderer(mRenderer);
setContentView(glSurfaceView);
//glSurfaceView.setOnTouchListener(this);
}
@Override
protected void onPause() {
super.onPause();
if (glSurfaceView != null) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (glSurfaceView != null) {
glSurfaceView.onResume();
}
}
}
美中不足的是,为了能够实时显示直线的轨迹,其实每次最后一条线绘制了两次,按下去拖动的时候显示的是,draw的线条,手指离开时drawmodel才会把它的直线画出来,此时draw的直线也完成了绘制。
(PS:通过加粗线条以后,基本看不出来了,哈哈哈)
/***************绘制模型**************/
//StraightLine straightLine = new StraightLine();
currentLines = new StraightLine(new float[] {
0, 0, 0});
currentLines.draw(mProjMatrix,mVMatrix);
drawModel(gl);
/************************************/
之所以这么做是因为,引入同步机制以后,发现绘制轨迹的时候,由于只取起点与终点,没有中间的轨迹,虽然画的很准,但是在手指起来之前是看不到自己画的线的。
我知道我这么描述你们估计听不懂,想看看啥样子的可以把draw函数注释了自己看看,哈哈哈。如果有大佬知道怎么解决这个问题的欢迎评论。
参考文献如下:
[1]: https://blog.csdn.net/soely/article/details/79183525
[2]: https://blog.csdn.net/cjzjolly/article/details/81667087?utm_medium=distribute.pc_relevant_right.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param_right&depth_1-utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param_right