从director.js中的 renderer.render(this._scene,this._deltaTime)就进入了cocos里面的渲染流程了;renderer的定义可以在 renderer/index.js里面找到:
比较重要的成员变量:device,_forward(前向渲染,将试图独享显示到屏幕上依赖device),_flow(渲染流),_handle(渲染前的合批操作,会检查是否符合合批的操作,优化性能,drawcall)
device: 负责针对不同平台的渲染设配的统一接口,里面有webgl的操作,包括上传uniform,drawElement也就是调用webgl的接口的核心类,我就不整篇的贴代码了,我挑选主要的来看看:
这是我整理的device类的脑图:

下面我选择一些重要的代码说说哈哈:
首先是Device的构造函数:里面有些注释方便大家理解
/**
* @param {HTMLElement} canvasEL
* @param {object} opts
*/
constructor(canvasEL, opts) {
let gl;
// default options
opts = opts || {};
if (opts.alpha === undefined) {
opts.alpha = false;
}
if (opts.stencil === undefined) {
opts.stencil = true;
}
if (opts.depth === undefined) {
opts.depth = true;
}
if (opts.antialias === undefined) {
opts.antialias = false;
}
// NOTE: it is said the performance improved in mobile device with this flag off.
if (opts.preserveDrawingBuffer === undefined) {
opts.preserveDrawingBuffer = false;
}
try {
gl = canvasEL.getContext('webgl', opts)
|| canvasEL.getContext('experimental-webgl', opts)
|| canvasEL.getContext('webkit-3d', opts)
|| canvasEL.getContext('moz-webgl', opts);
} catch (err) {
console.error(err);
return;
}
// No errors are thrown using try catch
// Tested through ios baidu browser 4.14.1
if (!gl) {
console.error('This device does not support webgl');
}
// statics
/**
* @type {WebGLRenderingContext}
*/
this._gl = gl;
this._extensions = {};
// 容量
this._caps = {}; // capability
// 状态统计
this._stats = {
texture: 0,
vb: 0,
ib: 0,
drawcalls: 0,
};
// https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API/Using_Extensions
this._initExtensions([
// 各向异性过滤 提升图片质量
'EXT_texture_filter_anisotropic',
// 提供额外的shader 图片采样方法 lod : level of detail 多细节层次 LOD就是为了支持当物体远离观察者或者物体的重要程度不同,位置不同,速度不同或者视角相关的参数不同需要减少渲染3D模型的复杂度
'EXT_shader_texture_lod',
// 与lod配合使用 可以使用shader dFdx,dFdy,fwidth方法
'OES_standard_derivatives',
// 暴露图片浮点数像素值
'OES_texture_float',
// 允许用浮点数像素值对图片进行过滤 在gl.texParameter中设置 gl.LINEAR...其中的一个
'OES_texture_float_linear',
/**
* var ext = gl.getExtension('OES_texture_half_float');
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, ext.HALF_FLOAT_OES, image);
*
*/
'OES_texture_half_float',
'OES_texture_half_float_linear',
// 提供顶点数组对象
/**
*
* var oes_vao_ext = gl.getExtension('OES_vertex_array_object');
var vao = oes_vao_ext.createVertexArrayOES();
oes_vao_ext.bindVertexArrayOES(vao);
*
*/
'OES_vertex_array_object',
/**
* 压缩纹理减少了在 GPU 上存储纹理所需的内存量,允许更高分辨率的纹理或更多相同分辨率的纹理。
*/
'WEBGL_compressed_texture_atc',
'WEBGL_compressed_texture_etc',
'WEBGL_compressed_texture_etc1',
'WEBGL_compressed_texture_pvrtc',
'WEBGL_compressed_texture_s3tc',
'WEBGL_depth_texture',
'WEBGL_draw_buffers',
]);
this._initCaps();
this._initStates();
// runtime
State.initDefault(this);
this._current = new State(this);
this._next = new State(this);
this._uniforms = {}; // name: { value, num, dirty }
this._vx = this._vy = this._vw = this._vh = 0;
this._sx = this._sy = this._sw = this._sh = 0;
this._framebuffer = null;
//
this._enabledAttributes = new Array(this._caps.maxVertexAttribs);
this._newAttributes = new Array(this._caps.maxVertexAttribs);
for (let i = 0; i < this._caps.maxVertexAttribs; ++i) {
this._enabledAttributes[i] = 0;
this._newAttributes[i] = 0;
}
}
核心方法: draw: 注意cur和next的交换,我现在的理解是将一些gl渲染数据写入到next之后,再进行交换,然后清空next的所有状态
/**
* @method draw
* @param {Number} base
* @param {Number} count
*/
draw(base, count) {
const gl = this._gl;
let cur = this._current;
let next = this._next;
// commit blend
_commitBlendStates(gl, cur, next);
// commit depth
_commitDepthStates(gl, cur, next);
// commit stencil
_commitStencilStates(gl, cur, next);
// commit cull
_commitCullMode(gl, cur, next);
// commit vertex-buffer
_commitVertexBuffers(this, gl, cur, next);
// commit index-buffer
if (cur.indexBuffer !== next.indexBuffer) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, next.indexBuffer && next.indexBuffer._glID !== -1 ? next.indexBuffer._glID : null);
}
// commit program
let programDirty = false;
if (cur.program !== next.program) {
if (next.program._linked) {
gl.useProgram(next.program._glID);
} else {
console.warn('Failed to use program: has not linked yet.');
}
programDirty = true;
}
// commit texture/sampler
_commitTextures(gl, cur, next);
// commit uniforms
for (let i = 0; i < next.program._uniforms.length; ++i) {
let uniformInfo = next.program._uniforms[i];
let uniform = this._uniforms[uniformInfo.name];
if (!uniform) {
// console.warn(`Can not find uniform ${uniformInfo.name}`);
continue;
}
if (!programDirty && !uniform.dirty) {
continue;
}
uniform.dirty = false;
// TODO: please consider array uniform: uniformInfo.size > 0
let commitFunc = (uniformInfo.size === undefined) ? _type2uniformCommit[uniformInfo.type] : _type2uniformArrayCommit[uniformInfo.type];
if (!commitFunc) {
console.warn(`Can not find commit function for uniform ${uniformInfo.name}`);
continue;
}
commitFunc(gl, uniformInfo.location, uniform.value);
}
if (count) {
// drawPrimitives
if (next.indexBuffer) {
gl.drawElements(
this._next.primitiveType,
count,
next.indexBuffer._format,
base * next.indexBuffer._bytesPerIndex
);
} else {
gl.drawArrays(
this._next.primitiveType,
base,
count
);
}
// update stats
this._stats.drawcalls++;
}
// TODO: autogen mipmap for color buffer
// if (this._framebuffer && this._framebuffer.colors[0].mipmap) {
// gl.bindTexture(this._framebuffer.colors[i]._target, colors[i]._glID);
// gl.generateMipmap(this._framebuffer.colors[i]._target);
// }
// reset states 交换缓冲区将next缓冲区显示到屏幕上
cur.set(next);
// 清空next缓冲区等待写入新的数据
next.reset();
}
本文详细探讨了Director.js中的renderer.render()方法如何触发Cocos渲染流程,并重点剖析了device类的构造函数和核心draw方法,涉及WebGL配置、纹理处理、顶点缓冲和渲染状态管理。通过核心代码实例,揭示了设备兼容性和性能优化关键.

5755

被折叠的 条评论
为什么被折叠?



