OpenGL之構(gòu)建簡單物體

OpenGL之基礎(chǔ)
OpenGL之繪制簡單形狀
OpenGL之顏色
OpenGL之調(diào)整屏幕寬高比
OpenGL之三維
OpenGL之紋理

三角形帶

要構(gòu)建圓兰迫,可以使用一個(gè)三角形扇

要構(gòu)建圓柱的側(cè)面,可以使用三角形帶(Triangle Strip)涯塔。與三角形扇一樣声登,三角形帶讓我們定義多個(gè)三角形而不用重復(fù)那些三角形中共有的點(diǎn)侣滩,如圖:


image-20211221211226581.png

三角形帶的前三個(gè)頂點(diǎn)定義了第一個(gè)三角形,之后的每個(gè)額外的頂點(diǎn)都定義了另外的一個(gè)三角形罢绽,為了使用三角形帶定義這個(gè)圓柱體的側(cè)面浴讯,只需要把這個(gè)帶繞成一個(gè)管子擎厢,并確保最后兩個(gè)頂點(diǎn)與最前面的兩個(gè)頂點(diǎn)一致

添加幾何圖形的類

public class Point {
    private float mX;
    private float mY;
    private float mZ;

    public Point(float x, float y, float z) {
        this.mX = x;
        this.mY = y;
        this.mZ = z;
    }

    /**
     * 沿著y軸移動(dòng)點(diǎn)
     * @param y
     * @return
     */
    public Point translateY(float y) {
        return new Point(mX, mY + y, mZ);
    }

    public float getX() {
        return mX;
    }

    public float getY() {
        return mY;
    }

    public float getZ() {
        return mZ;
    }
}
public class Circle {
    private Point mCenter;
    private float mRadius;

    /**
     * @param center
     * @param radius
     */
    public Circle(Point center, float radius) {
        this.mCenter = center;
        this.mRadius = radius;
    }

    public Point getCenter() {
        return mCenter;
    }

    public float getRadius() {
        return mRadius;
    }
}
public class Cylinder {
    private Circle mCircle;
    private float mHeight;

    public Cylinder(Circle circle, float height) {
        mCircle = circle;
        mHeight = height;
    }

    public Circle getCircle() {
        return mCircle;
    }

    public float getHeight() {
        return mHeight;
    }
}

添加物體構(gòu)建器

public class ObjectBuilder {
    private static final int FLOAT_PER_VERTEX = 3;
    // 用于保存生成的頂點(diǎn)數(shù)據(jù)和繪制命令
    protected GeneratedData mGeneratedData;
    // 記錄下一個(gè)頂點(diǎn)的位置
    protected int mOffset;
    // 保存頂點(diǎn)的位置
    protected float[] mVertexData;

    public ObjectBuilder(int vertexSize) {
        mVertexData = new float[vertexSize * FLOAT_PER_VERTEX];
        mGeneratedData = new GeneratedData(mVertexData);
    }

    /**
     * 創(chuàng)建冰球
     * @param circle
     * @param cylinder
     * @param numPoints
     * @return
     */
    public static GeneratedData createPuck(Circle circle, Cylinder cylinder, int numPoints) {
        int size = sizeOfVertexInCircle(numPoints) + sizeOfVertexInCylinder(numPoints);
        ObjectBuilder builder = new ObjectBuilder(size);
        builder.addCircle(circle, numPoints);
        builder.addCylinder(cylinder, numPoints);
        return builder.build();
    }

    /**
     * 創(chuàng)建木槌
     * @param topCircle
     * @param topCylinder
     * @param bottomCircle
     * @param bottomCylinder
     * @param numPoints
     * @return
     */
    public static GeneratedData createMallet(Circle topCircle, Cylinder topCylinder,
                                             Circle bottomCircle, Cylinder bottomCylinder, int numPoints) {
        int size = (sizeOfVertexInCircle(numPoints) + sizeOfVertexInCylinder(numPoints)) * 2;
        ObjectBuilder builder = new ObjectBuilder(size);
        builder.addCircle(topCircle, numPoints);
        builder.addCylinder(topCylinder, numPoints);
        builder.addCircle(bottomCircle, numPoints);
        builder.addCylinder(bottomCylinder, numPoints);
        return builder.build();
    }

    /**
     * 生成頂點(diǎn)數(shù)據(jù)和繪畫指令
     *
     * @return
     */
    public GeneratedData build() {
        return mGeneratedData;
    }

    /**
     * 添加圓柱體側(cè)面
     *
     * @param size
     * @param cylinder
     */
    protected void addCylinder(Cylinder cylinder, int size) {
        int startDrawCylinderOffset = mOffset / FLOAT_PER_VERTEX;
        //添加圓柱體側(cè)面的頂點(diǎn)數(shù)據(jù)
        addCylinderVertex(cylinder, size);
        //畫圓柱體側(cè)面命令
        mGeneratedData.addDrawCommand(new GeneratedData.DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_STRIP, startDrawCylinderOffset, sizeOfVertexInCylinder(size));
            }
        });
    }

    /**
     * 添加圓柱體頂端的圓
     *
     * @param size
     * @param circle
     */
    protected void addCircle(Circle circle, int size) {
        int startDrawCircleOffset = mOffset / FLOAT_PER_VERTEX;
        //添加圓柱體頂部圓的頂點(diǎn)數(shù)據(jù)
        addCircleVertex(circle, size);
        //畫圓命令
        mGeneratedData.addDrawCommand(new GeneratedData.DrawCommand() {
            @Override
            public void draw() {
                glDrawArrays(GL_TRIANGLE_FAN, startDrawCircleOffset, sizeOfVertexInCircle(size));
            }
        });
    }

    /**
     * 添加圓柱體頂部圓的頂點(diǎn)數(shù)據(jù)
     *
     * @param circle
     * @param size
     */
    private void addCircleVertex(Circle circle, int size) {
        Point center = circle.getCenter();
        float radius = circle.getRadius();

        //三角形扇的第一個(gè)點(diǎn)究流,中間的點(diǎn)
        mVertexData[mOffset++] = center.getX();
        mVertexData[mOffset++] = center.getY();
        mVertexData[mOffset++] = center.getZ();

        double angleInRadian;
        for (int i = 0; i <= size; i++) {
            angleInRadian = (2 * Math.PI / size) * i;
            mVertexData[mOffset++] = (float) (center.getX() + radius * Math.cos(angleInRadian));
            mVertexData[mOffset++] = center.getY();
            mVertexData[mOffset++] = (float) (center.getZ() + radius * Math.sin(angleInRadian));
        }
    }

    /**
     * 添加圓柱體側(cè)面的頂點(diǎn)數(shù)據(jù)
     *
     * @param cylinder
     * @param size
     */
    private void addCylinderVertex(Cylinder cylinder, int size) {
        Point center = cylinder.getCircle().getCenter();
        float radius = cylinder.getCircle().getRadius();
        float height = cylinder.getHeight();

        double angleInRadian;
        for (int i = 0; i <= size; i++) {
            angleInRadian = (2 * Math.PI / size) * i;
            //上面的頂點(diǎn)
            mVertexData[mOffset++] = (float) (center.getX() + radius * Math.cos(angleInRadian));
            mVertexData[mOffset++] = center.getY() + height;
            mVertexData[mOffset++] = (float) (center.getZ() + radius * Math.sin(angleInRadian));

            //下面的頂點(diǎn)
            mVertexData[mOffset++] = (float) (center.getX() + radius * Math.cos(angleInRadian));
            mVertexData[mOffset++] = center.getY();
            mVertexData[mOffset++] = (float) (center.getZ() + radius * Math.sin(angleInRadian));
        }
    }


    /**
     * 圓頂點(diǎn)數(shù)據(jù)大小
     *
     * @param size
     * @return
     */
    protected static int sizeOfVertexInCircle(int size) {
        return 1 + size + 1;
    }

    /**
     * 圓柱側(cè)邊頂點(diǎn)數(shù)據(jù)大小
     *
     * @param size
     * @return
     */
    protected static int sizeOfVertexInCylinder(int size) {
        return (size + 1) * 2;
    }
}
public class GeneratedData {
    private List<DrawCommand> mDrawCommandList = new ArrayList<>();
    private float[] mVertexData;

    public GeneratedData(float[] vertexData) {
        mVertexData = vertexData;
    }

    public void addDrawCommand(DrawCommand drawCommand) {
        mDrawCommandList.add(drawCommand);
    }

    public float[] getVertexData() {
        return mVertexData;
    }

    public List<DrawCommand> getDrawCommandList() {
        return mDrawCommandList;
    }

    public interface DrawCommand {
        void draw();
    }
}

新建冰球類

同樣的方式去更新木槌類

public class Puck {
    private final GeneratedData mGeneratedData;
    private final Point centerPoint = new Point(0f, 0f, 0f);
    private VertexArray mVertexArray;

    public Puck(float radius, float height, int size) {
        // 創(chuàng)建圓柱體頂部圓
        Point topCircleCenter = new Point(centerPoint.getX(), centerPoint.getY(), centerPoint.getZ());
        Circle circle = new Circle(topCircleCenter, radius);

        //創(chuàng)建圓柱體側(cè)面
        Circle cylinderCircle = new Circle(centerPoint, radius);
        Cylinder cylinder = new Cylinder(cylinderCircle, height);

        mGeneratedData = ObjectBuilder.createPuck(circle, cylinder, size);

        mVertexArray = new VertexArray(mGeneratedData.getVertexData());
    }

    /**
     * 頂點(diǎn)數(shù)據(jù)綁定到OpenGL程序
     *
     * @param colorProgram
     */
    public void bindData(ColorProgram colorProgram) {
        mVertexArray.setVertexAttribPointer(
                0,
                colorProgram.getAPosition(),
                POSITION_COMPONENT_COUNT,
                0
        );
    }

    /**
     * 畫冰球
     */
    public void draw() {
        List<GeneratedData.DrawCommand> drawCommandList = mGeneratedData.getDrawCommandList();
        for (GeneratedData.DrawCommand drawCommand : drawCommandList) {
            drawCommand.draw();
        }
    }
}

更新著色器

頂點(diǎn)著色器

attribute vec4 a_Position;
uniform mat4 u_Matrix;

void main(){
    gl_Position=u_Matrix*a_Position;
    gl_PointSize=10.0;
}

片段著色器

precision mediump float;
uniform vec4 u_Color;
void main(){
    gl_FragColor=u_Color;
}

更新冰球?qū)?yīng)的程序

public class ColorProgram extends BaseProgram {
    private final int mAPosition;
    private final int mUMatrix;
    private final int mUColor;

    public ColorProgram(Context context) {
        super(context, R.raw.buildobjects6_color_vertex, R.raw.buildobjects6_color_fragment);
        mAPosition = glGetAttribLocation(mProgram, A_POSITION);
        mUColor = glGetUniformLocation(mProgram, U_COLOR);
        mUMatrix = glGetUniformLocation(mProgram, U_MATRIX);
    }

    public void setUniforms(float[] matrix, float r, float g, float b) {
        glUniformMatrix4fv(mUMatrix, 1, false, matrix, 0);
        glUniform4f(mUColor, r, g, b, 1f);
    }

    public int getAPosition() {
        return mAPosition;
    }
}

矩陣總結(jié)

矩陣 說明
模型矩陣 移動(dòng)辣吃、旋轉(zhuǎn)、平移單獨(dú)的某個(gè)物體
視圖矩陣 平等地影響場景中的每一·個(gè)物體芬探,相當(dāng)于移動(dòng)相機(jī)
投影矩陣 幫助創(chuàng)建三維的幻象神得,通常只有當(dāng)屏幕變換方位時(shí),它才會(huì)變化

視圖矩陣

使用 Matrix.setLookAtM 方法創(chuàng)建視圖矩陣

方法原型

public static void setLookAtM(float[] rm, int rmOffset,
        float eyeX, float eyeY, float eyeZ,
        float centerX, float centerY, float centerZ, float upX, float upY,
        float upZ) {

參數(shù)說明

參數(shù) 說明
float[] rm 最終生成的視圖矩陣數(shù)組偷仿,長度至少容納16個(gè)元素哩簿,以便它能存儲(chǔ)視圖
int rmOffset 從 rmOffset 的這個(gè)偏移值開始存進(jìn)rm
float eyeX, float eyeY, float eyeZ, 這是眼睛所在的位置,場景中的所有東西看起來都像是從這個(gè)點(diǎn)觀察它們一樣
float centerX, float centerY, float centerZ 這是眼睛正在看的地方酝静,這個(gè)位置出現(xiàn)在整個(gè)場景的中心
float upX, float upY, float upZ 你的頭指向的地方节榜,upY的值為1意味著你的頭筆直指向Y軸上方

坐標(biāo)變換總結(jié)

vertex(clip) = ProjectionMatrix * ViewMatrix * ModelMatrix * vertex(model)

修改渲染器

public class MyRenderer implements GLSurfaceView.Renderer {
    private Context mContext;

    // 保存模型矩陣
    private float[] modelMatrix = new float[16];
    // 視圖矩陣
    private float[] viewMatrix = new float[16];
    // 保存投影矩陣
    private float[] projectionMatrix = new float[16];
    // 保存視圖矩陣和投影矩陣的乘積
    private float[] viewProjectionMatrix = new float[16];
    // 保存模型矩陣、視圖矩陣和投影矩陣的乘積
    private float[] modelViewProjectionMatrix = new float[16];

    private TextureProgram mTextureProgram;
    private ColorProgram mColorProgram;
    private Table mTable;
    private Mallet mMallet;
    private int mTextureId;
    private Puck mPuck;

    public MyRenderer(Context context) {
        mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        glClearColor(0f, 0f, 0f, 0f);
        mTextureProgram = new TextureProgram(mContext);
        mColorProgram = new ColorProgram(mContext);
        // 創(chuàng)建物體
        mTable = new Table();
        mMallet = new Mallet(0.08f, 0.05f, 0.04f, 0.10f, 64);
        mPuck = new Puck(0.06f, 0.05f, 64);
        mTextureId = TextureHelper.loadTexture(mContext, R.drawable.air_hockey_surface);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        glViewport(0, 0, width, height);

        MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / height, 1, 10);

        // 創(chuàng)建視圖矩陣
        Matrix.setLookAtM(viewMatrix, 0, 0, 1.2f, 2.2f, 0, 0, 0, 0, 1, 0);
        Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        glClear(GL_COLOR_BUFFER_BIT);
        // 繪制桌子
        positionTableOnScreen();
        mTextureProgram.useProgram();
        mTextureProgram.setUniforms(modelViewProjectionMatrix, mTextureId);
        mTable.bindData(mTextureProgram);
        mTable.draw();

        // 繪制木槌
        positionObjectOnScreen(0f, 0f, 0.4f);
        mColorProgram.useProgram();
        mColorProgram.setUniforms(modelViewProjectionMatrix, 0f, 1f, 0f);
        mMallet.bindData(mColorProgram);
        mMallet.draw();

        // 繪制冰球
        positionObjectOnScreen(0f, 0f, 0f);
        mColorProgram.useProgram();
        mColorProgram.setUniforms(modelViewProjectionMatrix, 1f, 0f, 0f);
        mPuck.bindData(mColorProgram);
        mPuck.draw();
    }

    /**
     * 桌子原來是以x和y坐標(biāo)定義的别智,因此要使它平放在地上宗苍,我們需要讓它繞x軸向后旋轉(zhuǎn)90度
     * 不需要把桌子平移一定的距離,因?yàn)槲覀兿胍雷釉谑澜缱鴺?biāo)里保持在位置(0,0,0)
     * 并且視圖矩陣已經(jīng)想辦法使桌子對我們可見了
     */
    public void positionTableOnScreen() {
        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.rotateM(modelMatrix, 0, -90, 1, 0, 0);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix, 0, modelMatrix, 0);
    }

    /**
     * 將物體移動(dòng)到(x,y,z)的位置
     *
     * @param x
     * @param y
     * @param z
     */
    public void positionObjectOnScreen(float x, float y, float z) {
        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.translateM(modelMatrix, 0, x, y, z);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix, 0, modelMatrix, 0);
    }
}

效果

image-20211221222748898.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末薄榛,一起剝皮案震驚了整個(gè)濱河市讳窟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敞恋,老刑警劉巖丽啡,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異硬猫,居然都是意外死亡补箍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門啸蜜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馏予,“玉大人,你說我怎么就攤上這事盔性∠忌ィ” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵冕香,是天一觀的道長蛹尝。 經(jīng)常有香客問我,道長悉尾,這世上最難降的妖魔是什么突那? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮构眯,結(jié)果婚禮上愕难,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好猫缭,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布葱弟。 她就那樣靜靜地躺著,像睡著了一般猜丹。 火紅的嫁衣襯著肌膚如雪芝加。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天射窒,我揣著相機(jī)與錄音藏杖,去河邊找鬼。 笑死脉顿,一個(gè)胖子當(dāng)著我的面吹牛蝌麸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艾疟,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼祥楣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了汉柒?” 一聲冷哼從身側(cè)響起误褪,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碾褂,沒想到半個(gè)月后兽间,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡正塌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年嘀略,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乓诽。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帜羊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸠天,到底是詐尸還是另有隱情讼育,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布稠集,位于F島的核電站奶段,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剥纷。R本人自食惡果不足惜痹籍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晦鞋。 院中可真熱鬧蹲缠,春花似錦棺克、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渔肩,卻和暖如春因俐,著一層夾襖步出監(jiān)牢的瞬間拇惋,已是汗流浹背周偎。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撑帖,地道東北人蓉坎。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像胡嘿,于是被迫代替她去往敵國和親蛉艾。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容