OpenGL登堂入室之路3-画两个点

本文主要知识点:

  1. 如何设置顶点数据
  2. OpenGL错误检测
  3. OpenGL的坐标系统

添加一个顶点的数据

可能有些同学发现我在搞笑,一个点能画,两个点还不简单,你可能同我一样,直接在顶点数组里添加了一个点的三个坐标,一运行,crash

why?????

logcat里有报错,但是没有发现什么地方出错了,我们只能根据自己的改动查找相关的地方,修改了顶点数据,不可能导致加载着色器失败,相关语句排除。那就只剩下使用ByteBuffer装载数据(有错就会报出来了),glVertexAttribPointer,glDrawArrays。

OpenGL的方法在出错的时候不会报异常,而是要我们手动的调用另一个函数 GLES20.glGetError, 它会返回上一条OpenGL方法执行的错误码,通过判断错误码是否为GLES20.GL_NO_ERROR,判断是否上条语句发生错误。我们对他封装一下,在发生错误是报运行时异常:

1
2
3
4
5
6
7
8
static void checkGLError(String op) {
final int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) { // 有错误
String msg = op + ": glError 0x" + Integer.toHexString(error);
Log.e(TAG, "CheckGLError: " + msg);
throw new RuntimeException(msg);
}
}

我们在上面,glVertexAttribPointer() 和 glDrawArrays() 后调用用checkGLError(), 发现是 glVertexAttribPointer() 出了问题。通过错误号,在网上可以查到出错信息,你可以自省搜索,这里我们通过这个错误来学习glVertexAttribPointer()方法

1
2
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false,
3 * 4 , mVertexBuffer);

我们已经知道mPositionHandle相当于是一个指针,通过它,就可以为着色器中的属性设置值,而这个函数的作用正是规定怎样往着色器传递数据:

  1. mPositionHandle 指明数据要给谁,这里传的是顶点坐标数据,传给了shader里的vPosition
  2. 3 表示每个顶点传递的一组数据的元素个数,可以是1, 2, 3, 4
  3. GLES20.GL_FLOAT 表示传递的数据类型为float
  4. false 表示定点数据值不标准化(我也不知什么意思,若您知道请不吝赐教)
  5. 3 * 4 表示要传递的数据的大小, 我们之前传递一个点,每个点的3个float,每个float表示4byte
  6. mVertexBuffer 最后的一个当然是数据

问题就出第五个参数,参数 3 × 4, 3个float, 代表一个点的数据, 现在,我们在数组又添加了一个点的数据,应该是6 * 4, 再抽象一下,VERTEX.length * 4

现在运行看一下,发现只显示了一个点,为什么?

竟然也没有报错, 这个咋整,一个问题,之所以是问题,要么是我们掌握的信息不够,或者错误,要么是我们没高清信息之间的关系。好吧,我就不卖关子了,实际同上面的问题差不多。

1
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);

这个函数,原先第三个参数是1表示画一个点,现在,传了两个点的数据,要画两个点,所以为2, 如果抽象一下,VERTEX.length / 3

  1. GLES20.GL_POINTS 表示绘制离散的点,之后我们会了解到更多绘制模式
  2. 0 表示从我们之前提供的数据的第几个点的数据开始画,改成1试试吧[坏笑]
  3. 1 表示要绘制几次 尽管我们传了两个点的数据, 但是,如果我们从第0个开始画1个也就只显式一个,所以你懂得

下面是我们有画一个点到画两个点要改动的代码:

1
2
3
4
5
6
7
8
9
10
11

private static final float[] VERTEX = {
0, 0f, 0.0f,
0.5f, 0f, 0f,
};

GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false,
3 * 4 , mVertexBuffer);

GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 2);

OpenGL 坐标系

看看上面两顶点的数据,它们分别是两个顶点的xyz三个坐标,我们先只考虑二维平面,你可以看到显示的效果,(0, 0, 0)的点在中间,说明原点在屏幕中间。(0.5, 0f, 0f)的点在右侧,说明从原点向有为x轴正方向。
你可以试着改变一下坐标,看一下,很容易得到OpenGL的坐标系:

  1. 原点在中央
  2. x轴的范围是-1 ~ 1
  3. y轴的范围是-1 ~ 1
  4. 右上角的坐标为(1,1,0)

剩下一个Z轴坐标系,我相信你能通过自己探索研究出它的规则的。


end

OpenGL登堂入室之路2--画个点

稍等,扯一扯

OpenGL的学习,门槛就是特别高,因为有太多的概念。本教程的一个任务就是要降低它的门槛,怎么降低呢,就是一小步一小步的走,学习就是得有耐心。

走一小步,还不行,还得有及时的明显的反馈,所以,效果还得明显,这能做到吗?答案是“不仅能,而且一定能”。
大多数教程,一开始第一个要画的图形是三角形,原因很简单,因为OpenGL ES对OpenGL绘制图形的方式做了阉割,要绘制三维世界的一个面,最基本的图形就是三角形,但是为什么我们不能简单点,再简单点,绘制一个三维世界的一个点呢,一条线呢。

好的项目从好的结构开始

在我们开始画第一个点时,首先调整一下我们的项目结构,因为之后我们所有的绘制操作全在Render里,所以我们会单独定义一个Render类:MRender

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MRender implements GLSurfaceView.Renderer {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.e(TAG, "onSurfaceCreated");
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.e(TAG, "onSurfaceChanged");
}

@Override
public void onDrawFrame(GL10 gl) {
Log.e(TAG, "onDrawFrame");
GLES20.glClearColor(1f, 1f, 0f, 1f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
}
}

设置Render的地方变成了这样,我们以后几乎所有的工作就是改这个文件了

别忘了:

1
surfaceView.setRenderer(new MRender());

另外,还需要在它之前调用:

1
surfaceView.setEGLContextClientVersion(2);

设置OpenGL上下文的版本为2.0,不然以后的操作可能会失败

思考在前

我们先来自己想一下,要画一个点,我们首先要有一个三维坐标,还要有一个颜色值,然后就绘制了。

OpenGL 的绘制过程

三步走

那么在OpenGL里怎么实现呢?如果你还记得在门外看到的,那该多好:

  1. 加载顶点数据上传到GPU
  2. 加载着色器程序,编译后,上传到GPU
  3. 绘制

第一步,加载数据

1
2
3
4
5
6
7
private void loadVertex() {
mVertexBuffer = ByteBuffer.allocateDirect(VERTEX.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(VERTEX);
mVertexBuffer.position(0); // Android里OpenGL使用Buffer作为数据,而不是直接使用数组,至于buffer怎么使用以后会有专门的说明
}

第二步,加载着色器

以上代码实际上使用ByteBuffer包装了一下float数组,Android里OpenGL使用Buffer作为数据,而不是直接使用数组,至于buffer怎么使用以后会有专门的说明

下面是就是所谓的加载数据并上传到GPU,是调用了我们自定义的两个方法,见名知意:

1
2
3
4
5
6
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.e(TAG, "onSurfaceCreated");
loadVertex();
loadProgram();
}

下面详细说明这两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 加载程序
*/
private void loadProgram() {
mProgram = GLES20.glCreateProgram(); // 创建一个Program对象,此对象非java里的对象,而是OpenGL里的对象,返回一个int值作为该对象的引用
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); // 加载顶点着色器,对顶点做处理
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); // 加载颜色着色器,为什么叫颜色着色器,直译过来应该是片着色器呀?因为主要是作颜色变换
GLES20.glAttachShader(mProgram, vertexShader); // 将着色器绑定到程序对象上
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram); // 连接,编译程序,为什么编译呢,你一会看到那个C语言代码就知道了。

mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // 从着色器程序的到属性的位置,从而可以向该属性设置值
}

OpenGL里有对象的概念,此对象是一个数据结构,是OpenGL里专有的,并不是Java里的对象。
以上程序,通知OpenGL创建一个程序对象,并且还会创建两个着色器对象,在着色器对象编译以后,绑定到程序对象,注意,我们并没有操作对象本身,而是使用了一个id,它是OpenGL对象的引用。
上面方法最后拿到了一个表示着色器程序里一个属性的引用,从而可以在之后向这个属性赋值,很块就能看到了。

第三步,绘制

我们在onDrawFrame函数里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 清空颜色为黑色
GLES20.glClearColor(0f, 0f, 0f, 1f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(mProgram); // 使用程序, 还记的状态机吗?在调用这一句后,OpenGL相关的绘制操作就会基于这个Program

GLES20.glEnableVertexAttribArray(mPositionHandle); // 刚才的顶点位置属性,先使能
GLES20.glVertexAttribPointer(mPositionHandle, VERTEX.length, GLES20.GL_FLOAT, false,
VERTEX.length * 4 , mVertexBuffer); // 然后向这个属性设置数据,个参数什么意思呢?

GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1); // 这里是真正绘制的方法,GLES20.GL_POINTS表示绘制方式为绘制离散的点,而还有其他方式,比如最常用,绘制三角形,我们传三个顶点数据,就会绘制出一个三角形

GLES20.glDisableVertexAttribArray(mPositionHandle); // 使顶点属性不可用,这也是,状态机的操作

GLES20.glUseProgram(0); // 还原程序,不在使用mProgram

着色器语言

剩下的还有顶点数据和GLSL语言的两个字符串没有粘出来,着色器,以后会专门讲的,完整代码看git吧

这是个新发现

这个运行效果是屏幕中间一个点,哦,好吧,我错了,是一个矩形,而且很大,为什么呢?因为这个点太大了,为了让大家看清一点,我们在着色器程序里给点设置了100.0的大小值, 而我之前都是设置5的。这里我们发现,原来OpenGL里所说的点,竟然就是个矩形!

下一篇我们讲两个点,而且是可以设置颜色的。说道颜色,你可能突然想起了什么,为什么这一篇没有设颜色。还是因为保证代码简单。这里我们每次draw的时候清空颜色为黑色,然后是在颜色着色器里设置了点的颜色。不要着急,下一篇肯定讲,我保证:)


我们的这个教程,需要你的动手做,一定要实践。代码在这里

OpenGL登堂入室之路1--基本框架

声明 OpenGL ES 版本

这第一篇文章,我们先来构建一个Android项目,它包含了每个OpenGL ES项目必须的东西。
首先在AndroidManifest里添加如下:

1
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>

这里声明了应用需要OpenGL ES版本为2.0。(为什么版本要这样写呢?)
实际上,这个声明不是必须的,没有它也可以正常运行,他的作用是在Google Play下在应用时,Google Play会根据它,来判断设备是否能够安装该应用,比如你的手机不支持OpenGL ES 2.0,Google Play就不会让你下载该应用。

两个必须

下面开始介绍真正必须的两个东西:

  1. GLSurfaceView
  2. GLSurfaceView.Render

这一个类,一个接口,就是Android框架支持OpenGL的基础。
我们现在先了解GLSurfaceView是一个Android 的View,负责显示,而GLSurfaceView.Render从名字我们就知道它是负责渲染的,我们之后所有的工作几乎都在Render里。
还应该声明的一点是Render的方法都是在另一个线程,即非UI线程来执行的,不会阻塞UI线程。

动起手来

接下来,就开始写代码了:

第一步 先创建一个项目
第二步 在自动生成的布局文件里添加GLSurfaceView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.jone.roadtoopenglonandroid.MainActivity">

<android.opengl.GLSurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

第三步 在对应的Activity里findView,找到GLSurfaceView,并且给它设置Render

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GLSurfaceView surfaceView = (GLSurfaceView)findViewById(R.id.surfaceView);
surfaceView.setRenderer(new GLSurfaceView.Renderer() {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.e(TAG, "onSurfaceCreated");
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.e(TAG, "onSurfaceChanged");
}

@Override
public void onDrawFrame(GL10 gl) {
Log.e(TAG, "onDrawFrame");
GLES20.glClearColor(1f,1f,0f,1f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
}
});

三个方法

这里我们可以看到,Render接口有3个方法

  1. onSurfaceCreated Surface创建后调用一次
  2. onSurfaceChanged GLSurfaceView大小改变时会调用一次,刚创建的时候也会调用一次。
  3. onDrawFrame 绘制要显示的每一帧

我给每个方法都加了log,发现onSurfaceCreated 和 onSurfaceChanged的确是调用了一次,而onDrawFrame却不断调用

渲染模式

实际上这个行为是可以改变的,通过surfaceView.setRenderMode来设置渲染的模式:

  1. GLSurfaceView.RENDERMODE_WHEN_DIRTY 只在创建和调用GLSurfaceView的requestRender方法时才会导致渲染,即onDrawFrame被调用
  2. GLSurfaceView.RENDERMODE_CONTINUOUSLY 见名知意,会持续渲染,说道这我们也知道,GLSurfaceView的渲染模式默认值是RENDERMODE_CONTINUOUSLY

来它两句OpenGL尝尝鲜

你可能会发现,onDrawFrame里除了打印log,还调用两个语句。这两个语句就是OpenGL的语句了。
我们使用OpenGL ES2.0,所以所用使用OpenGL的地方都使用GLES20来调用静态属性或者方法。
你可能注意到Render的三个方法都有个参数GL10 gl,或许你已经知道,它是OpenGL ES 1.0的用法,忽略它就行了,OpenGL 2.0后都使用静态方法。

接着说这两个语句:

1
2
GLES20.glClearColor(1f,1f,0f,1f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

如果不设置,你可以试试,整个GLSurfaceView都是黑色的。而有这两句则会是黄色。为什么?
我们首先来说glClear的作用,从名称也可以了解一点它的作用,清空,因为onDrawFrame是多次调用的,如果不调用glClear,则每次绘制的内容都会叠加,这并不是我们期望的,而在每次绘制前glClear,则会清空上次的绘制内容。至于参数,通过标志为来标明要清空的缓存。以后会更深入的了解。
GLES20.glClearColor(1f,1f,0f,1f); 则决定了每次清空上次的绘制内容后显示什么颜色,参数分别为rgba,每个参数的取值为0~1f, 我们给r,g设置了1,所以是黄色,透明度设置1为不透明。

好,本文结束。


我们的这个教程,需要你的动手做,一定要实践。代码在这里

OpenGL登堂入室之路0

上一篇,我们看见了OpenGL的大门,接下来,我们就开始进入大门,走在了登堂入室之路上

我打算将这个教程作成一个系列,为什么呢?因为一直以来,在我想去学习Android上使用OpenGL时,除了官方的sample,就是官方sample的翻译,或者类似的博客,画个三角形,或者画个立方体,后来,有了Android平台的系列教程,但是是基于OpenGL ES1.0的,落后了。有一天我找到了一个教程,它是讲在Windows上开发OpenGL 3.0的教程,它所讲的内容还是比较简明的,但是并不太适合Android开发者,我希望其他Android开发的朋友不会像我一样,再去学习这个教程,所以我打算写一个Andoid平台的全面OpenGL教程。这个过程也是我自己学习的过程,这个过程肯定也是艰辛的,所以希望这条路上有更多的人参与进来,不断改进,完善,使得它成为Android上学习OpenGL的最佳之选。

我会在github上开一个项目,这个项目是会随着每一篇教程学到的知识而不断长大,完善的,每一篇对应git的一个分支(如果你不了解git,请看git-简明指南)

我们将在Android上使用OpenGL ES,OpenGL ES 是 OpenGL API的删减版本,专门支持嵌入式设备的版本。

Android现在支持OpenGL ES的几个版本如下:

  1. OpenGL ES 1.0 和 1.1 – Android 1.0 以上都支持
  2. OpenGL ES 2.0 – Android 2.2(API 8) 以上都支持 3. OpenGL ES 3.0 – Android 4.3(API 18) 以上都支持
  3. OpenGL ES 3.1 – Android 5.0(API 21) 以上都支持

我们选择 OpenGL ES 2.0 来学习。因为现在它最流行,3.0有些设备部支持,1.0已经落后了。

让我们迈开小步,一点一点成长吧

OpenGL 入门

OpenGL 入门(实际是看见大门)

一直对OpenGL感兴趣,但是之前了解过它两次,也写过例子,但是没弄懂,最近工作需要,学习了一下,找到了一个好的教程,才算弄明白了一点,这篇文章只是开个头,试着按照我的理解将OpenGL的基本原理讲明白,希望读者反馈,一块讨论:)

从哪开始

学习一个东西,最快的方式应该是先从宏观的角度去了解它,起点当然是官方网站和Wiki,这里是最正确的一手资料。

OpenGL 是 open graphics library 的缩写。从名字来看,它是一个库, 但是它只是一个 API(application programming interface),只是一套规范,用来渲染 2D 或者 3D 向量图形的。为什么是向量图形,以后会会讲到,先来把它的用途说完,它是通常用来与GPU交互,实现硬件加速。”通常”,你懂得他的意思吧?因为它就是一套接口,如果你要实现它不与GPU交互那也是可以的。要去实现它的是那些生产显卡GPU的厂商,这样,他的GPU被叫做支持OpenGL。

硬件加速是什么意思? 硬件加速是借助硬件模块来替代 运行在通用目的的CPU上的 软件算法。注意加粗的部分,没有这个修饰,就很难理解了,因为所有软件算法都是运行在硬件上的,是吧?这里的意思,CPU是通用目的的,而GPU是专门处理图像的,所以GPU的图像处理操作要比CPU块,不然也没它事了。

这样,我们知道OpenGL操作GPU, GPU在显卡上,显卡有现存,而我们平时编程只是跟内存打交道, 当然还有CPU,我们是摆脱不掉他的。所以我们在进行OpenGL开发时,就牵扯到CPU, GPU, 内存, 显存,还有文件存储系统。

状态机–给我发个红包,我笑给你看

OpenGL 本身是一个大的状态机,这只鸡有多大, 大到我们总是在它肚子里,哈哈,开个玩笑,不过也算贴切。

我们先来理解什么是状态机。 数学上有有限状态机, 用来描述有限个状态,而且这些状态之间会相互转换。 形象一点,一个烤面包机也算一个状态机,有没有插电两种状态,有没有打开开关两个状态, 有没有插入面包两个状态。这些状态,都会因为一些行为而改变,比如插入插销,烤面包机就由未插电状态变成插电状态。而某些状态会支持某些行为,如只有插电,插入面包,打开开关,这时才能够加热面包。
下面还有个图简单的不能再简单的状态机:


这是我画的,还挺有感觉:)

一个大开关,里边有三个按钮,这就表示了6种状态,而每个按钮的拨动,都会导致大开关的状态转换,这就是状态机。

OpenGL里有个Context的概念,而这个Context,中文译为上下文。我们在进行 OpenGL 开发时, 会设置某些选项,操作某些缓冲,这些操作并没有指明是对谁,但是实际上是改变了当前上下文的状态, 然后进行绘制,绘制出来的图像是那种状态下的样子。

然后,当切换了上下文时,之前的配置就都无效了。

这就像我们本来看着一个正在烤着面包的面包机,突然目光转向一个电都没插,开关没开的烤面包机,然后插电,插面包,打开,这一系列的操作的对象都不用指明是那个烤面包机,但是,读起来却明白是它,这就是上下文的作用。

对象–状态的集合,数据的容器

OpenGL 里也有对象, 但是此对象并不是面向对象编程语言里的对象,更不是你妈让你找的对象,而是一个可选项的集合,此集合是OpenGL状态的一个子集。如图:

我们可以把控制灯光的两个开关看做一个对象,控制风扇的一个开关看做一个对象,这样这个状态机就有两个对象。

无论何时何地,我们都像下面这样使用对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// OpenGL的状态
struct OpenGL_Context
{
...
object * object_Window_Target;
...
};

// 创建对象
GLuint objectId = 0;
glGenObject(1, &objectId);

// 把对象绑定到环境
glBindObject(GL_WINDOW_TARGET, objectId);

// 设置当前绑定到GL_WINDOW_TARGET的对象的选项
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);

// 把上下文目标重设为默认
glBindObject(GL_WINDOW_TARGET, 0);

(代码来自:五十弦翻译的教程,啥叫禁止演义)

OpenGL如何工作的

前面已经提到过,OpenGL总共牵扯到计算机的5个大部分,它们分别是:
CPU,内存,GPU,显存,外存, 主要干了三个大事:

  1. CPU将数据从内存复制到显存
  2. CPU将着色器程序上传到GPU
  3. 通知GPU运行着色器程序,处理数据

下面一个图来表示 OpenGL 是如何工作的:

根据以上我们说的三个大事,和图片所示,我们就可以解释几个语句块

上传顶点数据的代码:

1
2
3
4
5
6
7
8
9
10
11
12
// 这是在内存中的数据
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f, // Left
0.5f, -0.5f, 0.0f, // Right
0.0f, 0.5f, 0.0f // Top
};
GLuint VBO; // Vertex Buffer Object, 这是一个缓存对象
glGenBuffers(1, &VBO); // 这时候才真正在显存创建了一个对象,VBO是它在内存中的一个引用,这时候,也分配了空间也有引用,但是依然不能操作它,因为,它跟OpenGLContext还没有关系
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 这里真正产生了关系,将缓存对象绑定到了GL_ARRAY_BUFFER这么一个数组缓存目标上,为什么是目标,因为,之后我们就通过操作它来影响显存中的缓存对象。
// 这里数据传送路径是这样的:内存->CPU->GPU->显存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); // 这里解绑刚才的缓存对象,之后的对GL_ARRAY_BUFFER的操作就不会影响上边创建的缓存对象了。

上面我们上传了顶点数据到显存,下面我们上传着色器程序到GPU,着色器程序是什么,我们暂时只需要知道,它会在GPU执行,处理我们上传到显存的数据就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 通知GPU分配一个顶点类型的着色器
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 将内存里的源代码指定给它(不知道有没有上传到显存)
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
// 编译着色器
glCompileShader(vertexShader);

...省略颜色着色器的代码,一般都会有这两个着色器,也可能有其他

// 创建一个着色器程序
GLuint shaderProgram = glCreateProgram();
// 将顶点着色器跟颜色着色器添加到着色器程序
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
// 链接着色器程序
glLinkProgram(shaderProgram);
// 这样就准备好了着色器程序

下面是绘制代码部分:

1
2
3
4
5
6
glUseProgram(shaderProgram); // 通知GPU使用shaderProgram着色器程序, 这里算是切换上下文,之后的GPU运行的都是这个Program
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 设置当前的GL_ARRAY_BUFFER数组缓存对象, 这里也是切换上下文,之后使用的数组数据,来自VBO
glDrawArrays(GL_TRIANGLES, 0, 3); // 这时候,它会自动的去拿GL_ARRAY_BUFFER里的数据,使用shaderProgram这个着色器程序,进行绘制
glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑顶点数据
// 不在使用shaderProgram着色器程序
glUseProgram(shaderProgram);

当然,OpenGL的基本概念还有很多,这里这篇文章知识让你远远的看一看它的全貌,如果喜欢,请继续关注, 如果您能提出宝贵的意见,不胜感激。

在 Ubuntu 上

这篇文章记录了我在 Ubuntu 上初始环境的配置搭建步骤,跟遇到的问题,当然它们不仅仅适用于16.04,希望对您有所帮助

Android Studio

  • 下载Android Studio

  • 安装java sdk:使用apt安装openjdk-8-sdk,我安装java9失败,就安装了java8,也不行,然后把所有java相关的都删除了,然后安装8成功,9依然失败

  • Android Sdk里有些工具是32位的所以需要安装对x32位的支持:

    sudo apt-get install -y libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1

  • adb 竟然支持我的两个设备,之前是需要配置的!!

Vim

  • sudo apt install vim
  • 安装vim 插件管理器:pathogen Vundle
  • 安装vim 对 markdown的支持,因为写博客要用到markdown

Jekyll

  • Jekyll 是一个静态网站生成器,生成的静态网站可以放在github上
  • Jekyll 是使用ruby写的,所以先安装新版本的ruby,然后用gem安装jekyll

zsh

  • 据说zsh很强大,但是太复杂,这倒是可以理解的,但是的确因此让它不能流行

  • 于是oh-my-zsh出现,它让zsh不难么复杂,于是我们就用zsh来替代我们使用的bash

  • 如果你想让你的用户shell默认就是zsh:

    打开/etc/passwd
    将你的用户最后的bash改成zsh

这里应该有一个脚本来自动完成整套搭建过程的……

番茄工作法图解--心流

pic

有一种精神状态称为“心流”,它具有以下特点:明确的目标、集中、专注、自我意识消失、时间感扭曲、直接和即时反馈、能力水平与面对挑战的平衡、个人控制感、工作本身的内在奖励、行为与认知的合一。

心流是一种创造性的状态。如果保持心流的状态,效率不就提高了吗?不是的。心流状态下缺乏全局观念,创造力与统筹力难以并存。时不时地,我需要纵览全局进行战略决策,部署行动,以便在下一个心流期间能够全身心投入这项行动中。

在进入心流阶段之前,我扭启一个番茄钟,让它稍后唤醒我,这样我可以暂时换上战略眼光,观察全局,然后再次回到心流中——这就是节奏。另外要记住的是,获得心流状态,进入好整以暇、正襟危坐、全神贯注的姿势,要比东张西望等待灵感有效得多。

作一个 Remoter

远程办公,一直是我的向往,它是我成为一个自由开发者的过度阶段。

之前有段时间也关注过,知道这种方式叫做 soho,今天又看到了, 得知这种方式叫remote,还有一本书《Remote》,作者就是写《Rework》 的那两位,他们的公司就是远程办公的人组成的,
一直控制着人数,保持着小团队。

今天记下一些资源,以供自己和读者参考。

首先推荐37Signals的三本书:

《Getting Real》、《Rework》、《Remote》

然后是37Signals公司的博客:
https://m.signalvnoise.com/ 英文的

他们的创业方法,的确很适合个人开发者。

相关的网站:

http://remotechina.xyz/ 这个网站专门推广远程办公,不过好像还没发展起来

毕业一年,不甘于公司的制度,理想在。

“有梦为马,既可以朝九晚五,又可以浪迹天涯”

技术上升
远程办公
游历世界
自由职业者

<转>给年轻程序员的建议

偶尔的,我会被人问道:如何成为一名优秀的程序员,更或者,如何成为一名程序员。每次人们问起,我都力图给出不同的答案。因此,我的答案是各种各样的。下面就是我认为的成为一名优秀的程序员需要做的一些事情。记住,想成为一名程序员,很多条路可走,这里列出的只是我的方法。

  1. 解决你自己的问题
    从外面获取答案和自己解决问题、找到答案,这两种方式是有区别的。当你自己解决一个问题时,你不仅解决了这一个问题,也意味着解决了跟它类似的数百万问题。这是一个先投资后收获的过程。有时,你可能需要花2到3天的时间解决一个问题,这没什么;这是你的前期投资。

  2. 从做小程序开始
    我说的小程序,是指100行左右的。目前为止,《Java程序设计教程实验手册(Deitel and Deitel)》这本书是我见过的最好的编程书。从第一章到第十章,里面的所有习题都做一遍。当你做完了这些,你就对编程有了相当的掌握了。

  3. 仿造软件
    找一个你喜欢的网站或游戏,把它仿造出来。在你真正的想做一件项目前,先做完这个。这个能促使你学到前沿的编程技术,而且能让你更容易的被招聘公司选中。做3-5个这样的仿制项目后,你就能实现任意的你想要的东西了。

  4. 每个项目都要学到新东西
    每次项目都努力使用一些听到过但从未使用过的新东西。没有使用过Jquery,那下次项目中就使用它;没有试过测试驱动开发,下次项目就是你的实验品;你明白我的用意,对吗?

  5. 说行
    如果有人请求你为他做些什么东西,记得要说“行”——如果你从来没有给别人做过什么东西的话。我知道你可能会想,我自己还有无数的东西要做呢,但从经验来看,除了把主要时间花在自己有利可图的项目上外,你也应该花一些时间做一些慈善项目。这样做你会得到人们的认可,人们会想着你,如果有机会的会话,他们就会引荐你。

  6. 交结程序员朋友
    这是另外一个获得引荐机会的方法。总有机会,你的程序员朋友的盘子装满了,他们需要有人把多余的商业机会接下来。这时他们需要你出现。同样,当你的盘子装不下时,你也需要有人帮你把活接走。

  7. 成为一个领域专家
    广闻博识,同时要至少精通其中一项。编程世界很大,没有人能掌握所有东西,所以,要有一个专长。例如,成为一个本地应用或金融软件方面的专家

转载自:http://www.aqee.net/advice-to-young-programmers/

一个旅行

此刻,一个人在这如画的风景之中,冥想。

一年过去了,梦想,还在,近了,但却模糊。这应该算是第一次出来旅游,……,赶时间,切入正题。

毕业一年多了,人生才刚刚开始,我们应该开一个好头,每年都来总结一下,好让自己感受到进步,安慰一下脆弱的内心,同时,也忏悔一下,感恩一下,那些人,那些事。

2015过去了!2016来了!2016来了!

流水账启动:

去年十月份,转入安卓应用开发,当时是一种妥协,由一心往硬件发展,转向应用开发。但是“妥协,是因为有更在乎的”,最根本的,我要创造,硬件更有感觉,摸得着看得见,但是我学的软件,我在软件方面更专长。

从索尼出来的时候,找工作时有三个条件:小公司(之前算是在大公司待过了),非外包(之前确实是外包),应用开发(之前算是系统开发了),并且带着一个伟大的理想,我要去做有广大用户的软件,有一天我能看到别人在使用(到现在都没实现)。

第一份第二份工作跟了同一个CEO,公司都倒闭了。我三个月刚从容下来,倒闭了,三个月,刚要沉淀下来,倒闭了,想想有点遗憾。但是呢,身处互联网时代,刚出来,这么早就沉淀下去,肯定会被压成页岩,所以要感恩,感恩CEO,感恩CTO。

第一份工作让我了解了阅读器的核心技术,至于是什么,机密。第二份工作,让我分清了O2O和P2P,认识了几个朋友。

就这样,我深深的体会到了小创业公司是什么样的,于是我决心去大公司,这次找工作一定要从容点,淡定点,好好选一个大公司。

万万没想到,在脉脉上看到一个百度的招聘信息,于是就这么意外的得到了一次进百度的机会,然后就幸运的进来了,非常感谢岩哥。

进百度感受怎么样,一个字,累,加班严重,但是这算啥,咱刚出来,就得摸爬滚打的练,咱还想四十岁退休,去夏威夷晒着太阳敲代码呢!

工作就说这些,成长是肯定的,但也有遗憾,比如几套代码都没有到达优化阶段,没有将自己的所学所用都积累下来,没有专心的去研究通透一个大的技术,比如View视图系统,不过,那又怎样,我们才刚刚开始嘛!

下面到了生活思想价值观方面了。
宗教,一个陌生又熟悉的概念,陌生更多一些。信仰,一个熟悉又陌生的概念,不过熟悉更多一点。信仰包括宗教。啊,sorry,这个问题还是无法说得清楚。
但是,价值观是更底层的,价值观从没有变过,而且也是值得坚持的,因为它很好,因为它跟钱总不是太近,跟物质总不是太近。

以上都是在旅游前在家写的,是一种理想的情况以下是现实。

我计划了一个元旦旅行,路线是从北京做高铁到济南坐飞机到昆明坐飞机到深圳坐飞机到天津坐火车到北京,整个过程顺顺利利完成,为这点点赞

在家的时候我计划要去哪去哪,但是到了昆明时,蒙了,我心想我来干什么。面对一个陌生的地方,陌生的时间点,还真是茫然,我本想去公园转转,但是还是没淡定下来,然后打了个车,一块的还有另一个航班改时间的人,路上司机很好,给各种介绍,介绍住长途客运站比较方便,给介绍了一个路线,于是我满心感谢呀,最后来到一个旅馆住宿,旅馆张口100,我本可以拒绝,但是没有,于是住了一晚,是那种泡沫板房,有一种潮潮的味道,它有多差我都能忍,关键有其他问题,后面说。

住了一晚,我从美团把第二晚上的房子定了,第二天一大早,我就出门,去了石林,很棒!!很累!!

下午回到长途客运站,订的旅馆的房东去接的,很热情,又给介绍旅游景点,我跟他说了我第一晚的情况,他说在外面,凡是别人帮忙的,都会有回扣车站要是有个人领你去吃饭,可能要10块钱,如果你自己去就可能8块。这个房东的确很好,他的旅馆在美团评价挺高的。

第三天,下雨,睡到中午,然后吃饭,去市里玩,没地方可去,就去了动物园,我还是第一次去动物园。但是却相当不爽,下雨淋成了落汤鸡。

然后赶往飞机场,想去坐地铁,找了好久找到了,发现昆明只有三个地铁线,1号2号连着,6号通往机场,竟然不连着,这还真的没想到,于是走出去找公交,等到了长途客运站,七点多点,6号线没了,我都服气了

这就是我的旅行。

为什么有这个旅行?其实有很多原因:

我有假期,

毕业出来快两年了,说好的旅行呢?

答应小伙伴的邀请的行动呢?

还没坐过飞机!

看《心花路放》留下的后遗症……

接下来,就飞往深圳。去找小伙伴了。

来到深圳,感觉真心特别好,冬天,植物却茂盛的像夏天一样,路边或者是椰子,或者是芭蕉,或者是棕榈,反正是棒棒哒,公园里各种北方没有的树,小郭挨个都能说上名来,真是佩服。

晚上,租了个床位就住下了,两室一厅,住了二十来个人,竟然很多人是长住的,这又让我服了。

接下来,就是各种逛,各种吃,在小明和小郭的带领下把想吃的水果都吃了个遍,最后下定结论,还是苹果香蕉橘子桃好吃

这俩家伙有点太过分,让我玩着,吃着,喝着,还得拿着上海的小伙伴们,就这个标准就行

在昆明和在深圳两地的处境,真心感受到,在外靠朋友,如果陌生的地方有个熟悉的人,是多么好

其实旅行是一种心态,本想升华一下……

题外话:其实我们都很优秀,只是不喜欢装饰(有点心虚,已经学着去装了)