分析glmark2的scene-buffer
这里面有三个类:WaveMesh、SceneBufferPrivate和SceneBuffer,组合关系依次为:SceneBuffer包含SceneBufferPrivate,SceneBufferPrivate包含WaveMesh。按照从大到小依次分析:
SceneBuffer类
用的是scene的接口,和其他的类一样,包含构造函数、析构函数、supported、load、unload、setup、teardown、update、draw和validate。
构造函数
new 一个 SceneBufferPrivate 给 priv_,然后是设置 option 参数,这里给的值还是默认值。
Option | Value | 描述 | 范围 |
---|---|---|---|
interleave | false | 是否顶点属性数据放一个数组中 | false,true |
update-method | map | 顶点数据更新的方法 | false,true |
update-fraction | 1.0 | mesh length 每次递增的小数范围 | 0.0 到 1.0 |
update-dispersion | 0.0 | How dispersed the updates are [0.0 - 1.0] | 0.0 到 1.0 |
columns | 100 | The number of mesh subdivisions length-wise | 无 |
rows | 20 | The number of mesh subdisivisions width-wise | 无 |
buffer-usage | static | buffer使用方式 | static,stream,dynamic |
析构函数
删除priv_: delete priv_
supported
判断是否支持 glMapBuffer 和 glUnmapBuffer,返回true或false。判断条件:update-method 参数是 subdata,直接取 GLExtensions 中这两个函数指针,这两个在 gl-state 的 init_gl_extension 中判断,gl-state 根据不同egl、wgl和glx派生不同的子类,egl要读取extension,判断其中是否有GL_OES_mapbuffer,其他两个直接为true。
load
设置running_ : running_ = false;
unload
空函数
setup
- 调用 Scene::setup()
- 设置 option 参数 interleave 为 true
- 提取其他 option 参数的值,指定相关控制参数:update_method、 usage、update_fraction、update_dispersion、nlength 和 nwidth。
- 创建 WaveMesh 对象 priv_->wave = new WaveMesh,并设置相关参数:interleave、update_method 和 usage。
- 调用 priv_->wave->mesh().build_vbo() 创建vbo对象,并传输数据。数据在 vertex_arrays_ 中存放 ???????????
- priv_->wave->program().start() 使用program。
- 设置viewport:priv_->wave->program()[“Viewport”] = LibMatrix::vec2(canvas_.width(), canvas_.height());
- 关闭 cull face:glDisable(GL_CULL_FACE);
- 初始化帧数、running_ 和时间参数。
teardown
- 删除 priv_->wave
- 恢复 GL_CULL_FACE 状态
- 调用 Scene::teardown() 这个函数是空
update
- 调用 Scene::update(),这个更新时间和帧数。
- 更新时间 elapsed_time
- 调用 wave 的 update 函数:priv_->wave->update(elapsed_time);
draw
计算 ModelViewProjectionMatrix 并传入,然后调用 priv_->wave->mesh().render_vbo() 开始渲染。
validate
读坐标 (402, 189) 的像素值,和 ref(0x34, 0x99, 0xd7, 0xff) 比较判断。
SceneBufferPrivate
SceneBufferPrivate 是一个结构体,包含 WaveMesh
struct SceneBufferPrivate {
WaveMesh *wave;
SceneBufferPrivate() : wave(0) {}
~SceneBufferPrivate() { delete wave; }
};
WaveMesh 类
这个类创建 wave mesh,包含的方法有:update、mesh、program,剩下是私有方法:vertex_length_index、wave_func、displacement、create_program、create_mesh。
构造函数
作用: 构造函数生成wave。初始化wave相关参数,然后调用create_program和create_mesh。
输入参数:
length/width:total length/width of the grid (in model coordinates)
nlength/nwidth:the number of length-wise/width-wise grid subdivisions
模型网格的大小和纵横分割数
wavelength:the wave length as a proportion of the length 波形在网格中的长度
duty_cycle:占空比
初始化相关波形计算参数:
k
=
2
π
w
a
v
e
l
e
n
g
t
h
∗
l
e
n
g
t
h
k=\frac{2\pi}{wavelength * length}
k=wavelength∗length2π
p
e
r
i
o
d
=
2
π
k
=
w
a
v
e
l
e
n
g
t
h
∗
l
e
n
g
t
h
period=\frac{2\pi}{k}=wavelength * length
period=k2π=wavelength∗length
f
u
l
l
_
p
e
r
i
o
d
=
p
e
r
i
o
d
d
u
t
y
_
c
y
c
l
e
full\_period=\frac{period}{duty\_cycle}
full_period=duty_cycleperiod
速度:
v
e
l
o
c
i
t
y
=
0.1
∗
l
e
n
g
t
h
velocity=0.1*length
velocity=0.1∗length
位移:
d
i
s
p
l
a
c
e
m
e
n
t
=
n
l
e
n
g
t
h
+
1
displacement=nlength + 1
displacement=nlength+1
create_program
加载 buffer-wireframe.vert/frag
vertex shader:
attribute vec3 position;
// Coordinates of the triangle vertices this vertex belongs to
attribute vec3 tvertex0;
attribute vec3 tvertex1;
attribute vec3 tvertex2;
uniform vec2 Viewport;
uniform mat4 ModelViewProjectionMatrix;
varying vec4 dist;
void main(void)
{
// Get the clip coordinates of all vertices
vec4 pos = ModelViewProjectionMatrix * vec4(position, 1.0);
vec4 pos0 = ModelViewProjectionMatrix * vec4(tvertex0, 1.0);
vec4 pos1 = ModelViewProjectionMatrix * vec4(tvertex1, 1.0);
vec4 pos2 = ModelViewProjectionMatrix * vec4(tvertex2, 1.0);
// Get the screen coordinates of all vertices
vec3 p = vec3(0.5 * Viewport * (pos.xy / pos.w), 0.0);
vec3 p0 = vec3(0.5 * Viewport * (pos0.xy / pos0.w), 0.0);
vec3 p1 = vec3(0.5 * Viewport * (pos1.xy / pos1.w), 0.0);
vec3 p2 = vec3(0.5 * Viewport * (pos2.xy / pos2.w), 0.0);
// Get the vectors representing the edges of the current
// triangle primitive. 'vN' is the edge opposite vertex N.
vec3 v0 = p2 - p1;
vec3 v1 = p2 - p0;
vec3 v2 = p1 - p0;
// Calculate the distance of the current vertex from all
// the triangle edges. The distance of point p from line
// v is length(cross(p - p1, v)) / length(v), where
// p1 is any of the two edge points of v.
float d0 = length(cross(p - p1, v0)) / length(v0);
float d1 = length(cross(p - p2, v1)) / length(v1);
float d2 = length(cross(p - p0, v2)) / length(v2);
// OpenGL(ES) performs perspective-correct interpolation
// (it divides by .w) but we want linear interpolation. To
// work around this, we premultiply by pos.w here and then
// multiple with the inverse (stored in dist.w) in the fragment
// shader to undo this operation.
dist = vec4(pos.w * d0, pos.w * d1, pos.w * d2, 1.0 / pos.w);
gl_Position = pos;
}
fragment shader:
varying vec4 dist;
const vec4 LINE_COLOR = vec4(1.0);
const vec4 TRIANGLE_COLOR = vec4(0.0, 0.5, 0.8, 0.8);
void main(void)
{
// Get the minimum distance of this fragment from a triangle edge.
// We need to multiply with dist.w to undo the workaround we had
// to perform to get linear interpolation (instead of perspective correct).
float d = min(dist.x * dist.w, min(dist.y * dist.w, dist.z * dist.w));
// Get the intensity of the wireframe line
float I = exp2(-2.0 * d * d);
gl_FragColor = mix(TRIANGLE_COLOR, LINE_COLOR, I);
}
create_mesh
创建 grid mesh ,传递顶点参数和性数,调用的是mesh类,这列最顶点属性和参数做了管理:
vertex_format:glVertexpointer中垫高点的数据数,vec3、vec4等
attrib_locations:顶点属性的location,从program中拿去
对应使用方法:
std::vector<int> vertex_format;
vertex_format.push_back(3);
mesh_.set_vertex_format(vertex_format);
std::vector<GLint> attrib_locations;
attrib_locations.push_back(program_["position"].location());
mesh_.set_attrib_locations(attrib_locations);
然后调用 mesh_.make_grid(nlength, nwidth, length, width, 0.0, wave_grid_conf) 生成顶点数据。参数的含义:x、y方向的grid分割数量,x、y两个方向上grid的总长度,两个cell之间的间隔,grid配置函数。
void
Mesh::make_grid(int n_x, int n_y, double width, double height,
double spacing, grid_configuration_func conf_func)
{
double side_width = (width - (n_x - 1) * spacing) / n_x;
double side_height = (height - (n_y - 1) * spacing) / n_y;
for (int i = 0; i < n_x; i++) {
for (int j = 0; j < n_y; j++) {
LibMatrix::vec3 a(-width / 2 + i * (side_width + spacing),
height / 2 - j * (side_height + spacing), 0);
LibMatrix::vec3 b(a.x(), a.y() - side_height, 0);
LibMatrix::vec3 c(a.x() + side_width, a.y(), 0);
LibMatrix::vec3 d(a.x() + side_width, a.y() - side_height, 0);
if (!conf_func) {
std::vector<float> ul(vertex_size_);
std::vector<float> ur(vertex_size_);
std::vector<float> ll(vertex_size_);
std::vector<float> lr(vertex_size_);
set_attrib(0, a, &ul);
set_attrib(0, c, &ur);
set_attrib(0, b, &ll);
set_attrib(0, d, &lr);
next_vertex(); vertices_.back() = ul;
next_vertex(); vertices_.back() = ll;
next_vertex(); vertices_.back() = ur;
next_vertex(); vertices_.back() = ll;
next_vertex(); vertices_.back() = lr;
next_vertex(); vertices_.back() = ur;
}
else {
conf_func(*this, i, j, n_x, n_y, a, b, c, d);
}
}
}
}
顶点生成步骤:按照从左上到右下的顺序,计算四个点的位置,考虑每个 cell 的间距。调用wave_grid_conf 是将四边形分成两个三角形,按照顺序存放顶点。
析构函数
调用reset函数,中止和释放program,然后reset mesh的参数。
update
更新 wave 的顶点值。计算方法如下:
- 按照时间计算位移量,将位移方向用索引存储在一个ranges的pair数组vector总,将位移量存储在数组displacement_中。
for (size_t n = 0; n <= nlength_; n++) {
double d(displacement(n, elapsed));
if (d != displacement_[n]) {
if (ranges.size() > 0 && ranges.back().second == n - 1) {
ranges.back().second = n;
}
else {
ranges.push_back(
std::pair<size_t, size_t>(n > 0 ? n - 1 : 0, n)
);
}
}
displacement_[n] = d;
}
- 按照三角形顶点的排序,对ranges遍历,然后取出displacement_存入vertices中。
for (std::vector<std::pair<size_t, size_t> >::iterator iter = ranges.begin();
iter != ranges.end();
iter++)
{
/* First vertex of length index range */
size_t vstart(iter->first * nwidth_ * 6 + (iter->first % 2));
/*
* First vertex not included in the range. We should also update all
* vertices of triangles touching index i.
*/
size_t vend((iter->second + (iter->second < nlength_)) * nwidth_ * 6);
for (size_t v = vstart; v < vend; v++) {
size_t vt = 3 * (v / 3);
vertices[v][0 * 3 + 2] = displacement_[vertex_length_index(v)];
vertices[v][1 * 3 + 2] = displacement_[vertex_length_index(vt)];
vertices[v][2 * 3 + 2] = displacement_[vertex_length_index(vt + 1)];
vertices[v][3 * 3 + 2] = displacement_[vertex_length_index(vt + 2)];
}
/* Update pair with actual vertex range */
iter->first = vstart;
iter->second = vend - 1;
}
mesh_.update_vbo(ranges);
}
- 调用mesh_.update_vbo(ranges)更新顶点数据,创建vbo。
mesh
返回 mesh_
program
返回program_
reset
前面已经介绍
vertex_length_index
Calculates the length index of a vertex.计算一个顶点的在X方向的index
计算公式
v / (6 * nwidth_) + (v % 2)
wave_func
wave的三角函数
double r(fmod(x, wave_full_period_));
if (r < 0)
r += wave_full_period_;
/*
* Return either the sine value or 0.0, depending on the
* wave duty cycle.
*/
if (r > wave_period_)
{
return 0;
}
else
{
return 0.2 * std::sin(wave_k_ * r);
}
displacement
计算wave的位移
* @param n the length index
* @param elapsed the time elapsed since the beginning of the rendering
*
* @return the displacement at point n at time elapsed
*/
double displacement(size_t n, double elapsed)
{
double x(n * length_ / nlength_);
return wave_func(x - wave_velocity_ * elapsed);
}