继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

编写一个自己的3D渲染库(JS编写)——架构设计与整体框架实现

小昊子
关注TA
已关注
手记 12
粉丝 54
获赞 183

    最近一直对3D编程蛮感兴趣,想自己写一个3D渲染库,便有了这篇文章。

    首先推荐两本本书——《3D数学基础:图形与游戏开发》和《3D游戏编程大师技巧》。

    诸如3D算法在这两本书上都有介绍。

    3D渲染算法主要的数学利器就是线性代数和三角函数,数学方法不赘述,可以自行查看专业书籍查看。

    在此之前,我们将首先编写一个大体框架,将一个3D渲染类的简单部分编写好在进行数学库的开发

    首先我给这个3D渲染库的名字叫See3D。并且这个库是在canvas元素上渲染,所以可以编写如下代码

class See3D {
    constructor(dom = document.createElement("canvas")) {
        this.__dom = dom;
        this.__ctx = dom.getContext("2d");
    }
    width(w) {
        if (w === void(0)) {
            return this.__dom.width;
        }
        this.__dom.width = w;
        return this;
    }
    height(w) {
        if (w === void(0)) {
            return this.__dom.height;
        }
        this.__dom.height = w;
        return this;
    }
    full() {
        this
            .width(window.innerWidth)
            .height(window.innerHeight)
        ;
        return this;
    }
    get dom() {
        return this.__dom;
    }
    get ctx() {
        return this.__ctx;
    }
}
!function () {
    See3D.version = "v0.0.1";
    console.log("See3D engine (" + See3D.version + ") launched");
    See3D.DEBUG = true;
}();

    那么,我们接下来就可以编写数学库的内容了吗?

    当然不是,设计一个好项目,当然要有一个好架构,我们当然要有一个好的设计,在此,我设计了一个类来表示所有的库:

!function () {
    class Library {
        constructor(name) {
            this.name = name;// 库名
            this.defines = {};// 存储API
        }
        define(name, val) {
            this.defines[name] = val;// 添加API
            return this;
        }
        get(name) {// 获取API
            if (this.defines[name].private) {// 如果是私有的
                console.error(new Error("Error 201: It's a private value"));
            }
            return this.defines[name];
        }
        trans() {// 将defines中的所有API添加至该库的根下
            for (let i in this.defines) {
                this["$" + i] = this.defines[i];
            }
        }
        global() {// 将库中的所有API添加到全局中
            for (let i in this.defines) {
                globalThis[i] = this.defines[i];
            }
        }
    }
    See3D.Library = Library;
}();

    但是,我们该这么存储这些库呢?放在全局?当然不是,See3D还要有一个static熟悉__libraries存储所有的库并且提供static函数来进行这些操作

    添加后See3D类如下:

class See3D {
    constructor(dom = document.createElement("canvas")) {
        this.__dom = dom;
        this.__ctx = dom.getContext("2d");
        this.loadGlobal();
        this.bindLibrary("IO");
        this.bindLibrary("Math3D");
        this.sout = new See3D.IO.$sostream(this);
    }
    width(w) {
        if (w === void(0)) {
            return this.__dom.width;
        }
        this.__dom.width = w;
        return this;
    }
    height(w) {
        if (w === void(0)) {
            return this.__dom.height;
        }
        this.__dom.height = w;
        return this;
    }
    full() {
        this
            .width(window.innerWidth)
            .height(window.innerHeight)
        ;
        return this;
    }
    /**
     * @function loadGlobal
     * @desc 将所有全局库加载入该See3D实例
     */
    loadGlobal() {
        for (let i of See3D.__loads) {
            this.load(i);
        }
        return this;
    }
    bindLibrary(name) {// 将该库的所有API加入该See3D实例
        let lib = this[name];
        for (let i in lib.defines) {
            this[i] = lib.defines[i];
        }
    }
    /**
     * @function load
     * @param {string} name
     * @desc 加载指定库到See3D对象中
     */
    load(name) {
        this[name] = See3D.__libraries.get(name);
        return this;
    }
    get dom() {
        return this.__dom;
    }
    get ctx() {
        return this.__ctx;
    }
    /**
     * @function library
     * @param {See3D.Library} entry
     * @desc 添加See3D库
     */
    static library(entry) {
        // entry
        See3D.__libraries.set(entry.name, entry);
    }
    /**
     * @function load
     * @param {String} name
     * @desc 添加应默认自带的库
     */
    static load(name) {
        See3D.__loads.push(name);
    }
    /**
     * @function loadGlobal
     * @param {String} name
     * @desc 将该库导入到全局环境
     */
    static loadGlobal(name) {
        globalThis[name] = See3D.__libraries.get(name);
    }
    static lib(name) {
        See3D[name] = See3D.__libraries.get(name);
        return See3D.__libraries.get(name);
    }
}

!function () {
    See3D.version = "v0.0.1";
    console.log("See3D engine (" + See3D.version + ") launched");
    See3D.DEBUG = true;
    /**
     * @property
     * @private
     */
    See3D.__libraries = new Map();
    /**
     * @property
     * @private
     */
    See3D.__loads = [];

    /**
     * @class Library
     * @constructor
     * @desc Library类提供了制作See3D类的接口
     */
    class Library {
        constructor(name) {
            this.name = name;
            this.defines = {};
        }
        define(name, val) {
            this.defines[name] = val;
            return this;
        }
        get(name) {
            if (this.defines[name].private) {
                console.error(new Error("Error 201: It's a private value"));
            }
            return this.defines[name];
        }
        trans() {
            for (let i in this.defines) {
                this["$" + i] = this.defines[i];
            }
        }
        global() {
            for (let i in this.defines) {
                globalThis[i] = this.defines[i];
            }
        }
    }
    See3D.Library = Library;
}();

    然后,由于JS是弱类型语言,对于类型检查几乎没有,在不适用TS的情况下,可以让所有库中的类都继承自一个LibraryDefineObject,该类十分简单,就只有一个type属性

class LibraryDefineObject {
    constructor(type) {
        this.type = type;
    }
    transType() {
        return null;
    }
}

    接着添加一个类型检查和类型转换函数:

function checkType(obj, type) {
    return obj.type == type;
}
function translate(type, obj) {
    return obj.transType(type);
}

    最后,全部代码如下

/**
 * @class See3D
 * @constructor
 * @desc See3D类是可以将一个canvas变为See3D画布的类,一切操作都依靠它 \n
 * 也是所有See3D API的容器,相当于一个module
 */
class See3D {
    constructor(dom = document.createElement("canvas")) {
        this.__dom = dom;
        this.__ctx = dom.getContext("2d");
        this.loadGlobal();
        this.bindLibrary("IO");
        this.bindLibrary("Math3D");
        this.sout = new See3D.IO.$sostream(this);
    }
    width(w) {
        if (w === void(0)) {
            return this.__dom.width;
        }
        this.__dom.width = w;
        return this;
    }
    height(w) {
        if (w === void(0)) {
            return this.__dom.height;
        }
        this.__dom.height = w;
        return this;
    }
    full() {
        this
            .width(window.innerWidth)
            .height(window.innerHeight)
        ;
        return this;
    }
    /**
     * @function loadGlobal
     * @desc 将所有全局库加载入该See3D实例
     */
    loadGlobal() {
        for (let i of See3D.__loads) {
            this.load(i);
        }
        return this;
    }
    bindLibrary(name) {// 将该库的所有API加入该See3D实例
        let lib = this[name];
        for (let i in lib.defines) {
            this[i] = lib.defines[i];
        }
    }
    /**
     * @function load
     * @param {string} name
     * @desc 加载指定库到See3D对象中
     */
    load(name) {
        this[name] = See3D.__libraries.get(name);
        return this;
    }
    get dom() {
        return this.__dom;
    }
    get ctx() {
        return this.__ctx;
    }
    /**
     * @function library
     * @param {See3D.Library} entry
     * @desc 添加See3D库
     */
    static library(entry) {
        // entry
        See3D.__libraries.set(entry.name, entry);
    }
    /**
     * @function load
     * @param {String} name
     * @desc 添加应默认自带的库
     */
    static load(name) {
        See3D.__loads.push(name);
    }
    /**
     * @function loadGlobal
     * @param {String} name
     * @desc 将该库导入到全局环境
     */
    static loadGlobal(name) {
        globalThis[name] = See3D.__libraries.get(name);
    }
    static lib(name) {
        See3D[name] = See3D.__libraries.get(name);
        return See3D.__libraries.get(name);
    }
}

!function () {
    See3D.version = "v0.0.1";
    console.log("See3D engine (" + See3D.version + ") launched");
    See3D.DEBUG = true;
    /**
     * @property
     * @private
     */
    See3D.__libraries = new Map();
    /**
     * @property
     * @private
     */
    See3D.__loads = [];

    /**
     * @class Library
     * @constructor
     * @desc Library类提供了制作See3D类的接口
     */
    class Library {
        constructor(name) {
            this.name = name;
            this.defines = {};
        }
        define(name, val) {
            this.defines[name] = val;
            return this;
        }
        get(name) {
            if (this.defines[name].private) {
                console.error(new Error("Error 201: It's a private value"));
            }
            return this.defines[name];
        }
        trans() {
            for (let i in this.defines) {
                this["$" + i] = this.defines[i];
            }
        }
        global() {
            for (let i in this.defines) {
                globalThis[i] = this.defines[i];
            }
        }
    }
    // 所有的See3D库类接口都必须继承自该类
    class LibraryDefineObject {
        constructor(type) {
            this.type = type;
        }
        transType() {
            return null;
        }
    }
    function checkType(obj, type) {
        return obj.type == type;
    }
    function translate(type, obj) {
        return obj.transType(type);
    }
    See3D.Library = Library;
    See3D.LibraryDefineObject = LibraryDefineObject;
    See3D.checkType = checkType;
    See3D.translate = translate;
}();

    接下来,下一篇手记就将正式进入3D渲染的算法上了——实现一个3D数学库

    [第一篇完]

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP