webgl 3.畫一個點(diǎn)

one point.png
var VERTEX_SHADER_SOURCE =
    'void main() {\n' +
    '   gl_Position = vec4(0.0,0.0,0.0,1.0);\n' +
    '   gl_PointSize = 10.0;\n' +
    '}\n';

var FRAGMENT_SHADER_SOURCE =
    'void main() {\n' +
    '   gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n' +
    '}\n';

var canvas = document.getElementById("canvas");
var gl = canvas.getContext('webgl');

if (!initShaders(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)) {
    alert('Failed to init shaders');
}

gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);

function initShaders(gl, vertexShaderSource, fragmentShaderSource) {
    var program = createProgram(gl, vertexShaderSource, fragmentShaderSource);
    if (!program) {
        console.log('Failed to create program');
        return false;
    }

    gl.useProgram(program);
    gl.program = program;

    return true;
}

function loadShader(gl, type, source) {
    // create shader object
    var shader = gl.createShader(type);
    if (shader == null) {
        console.log('unable to create shader');
        return null;
    }

    // set shader source code
    gl.shaderSource(shader, source);

    // compile the shader
    gl.compileShader(shader);

    // check compile status
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
        var error = gl.getShaderInfoLog(shader);
        console.log('Failed to compile shader: ' + error);
        return null;
    }

    return shader;
}

function createProgram(gl, vertexShaderSource, fragmentShaderSource) {
    var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
    var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
    if (!vertexShader || !fragmentShader) {
        return null;
    }

    // create a program object
    var program = gl.createProgram();
    if (!program) {
        console.log('gl.createProgram failed');
        return null;
    }

    // attach  the shader objects
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    // link the program object
    gl.linkProgram(program);

    // check link status
    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
        var error = gl.getProgramInfoLog(program);
        console.log('Failed to link program: ' + error);
        gl.deleteProgram(program);
        gl.deleteShader(vertexShader);
        gl.deleteShader(fragmentShader);
        return null;
    }

    return program;
}

對于初學(xué)者來說 initShaders 的具體實現(xiàn)可以先忽略评甜,這個方法主要是編譯鏈接 shader 的。仔細(xì)讀一遍也基本能看懂仔涩,編譯鏈接 shader 是固定套路忍坷,基本都是這么寫的。

那么什么是 shader(著色器) 呢熔脂?

  • shader 其實就是運(yùn)行在 GPU 中的一段程序佩研,因為圖形學(xué)是對 GPU 進(jìn)行編程。
    圖形圖像從輸入到 GPU 到顯示在顯示器上霞揉,中間要經(jīng)過一系列的處理旬薯,這一系列的處理流程叫作 GPU 的 PipeLine (管線)。
  • GPU PipeLine 要求程序必須提供 Vertex shader 和 Fragment shader适秩。
  • Vertex shader 用來處理 vertex (頂點(diǎn))绊序,一個頂點(diǎn)中包含圖形的 position (位置) 、color 等信息秽荞。每一個頂點(diǎn)都會輸入到 Vertex shader 中執(zhí)行一次骤公,你可能會問一個圖形可能有成千上萬個頂點(diǎn),每個頂點(diǎn)都執(zhí)行一次 Vertex shader 是不是會很慢扬跋?因為 GPU 被設(shè)計成可以并行的處理數(shù)據(jù)阶捆,所以這些頂點(diǎn)并不是串行處理的,而是可以瞬間并行處理完畢胁住。這也是設(shè)計 GPU 的初衷趁猴,高效處理圖形圖像。
  • Fragment shader 用來處理 Fragment (片元) 彪见。fragment 就是顯示在屏幕上的 pixel (像素)儡司。 Fragment shader 用來計算每個像素的顏色,對于每個 fragment 都會執(zhí)行一次 Fragment shader余指。

來看看 shader 的代碼

// vertex shader
var VERTEX_SHADER_SOURCE =
    'void main() {\n' +
    '   gl_Position = vec4(0.0,0.0,0.0,1.0);\n' +
    '   gl_PointSize = 10.0;\n' +
    '}\n';

// fragment shader
var FRAGMENT_SHADER_SOURCE =
    'void main() {\n' +
    '   gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n' +
    '}\n';
  • shader 使用 GLSL ES (OpenGL ES shading language) 語言編寫的捕犬,這是一個類 C 語言跷坝,很容易看懂。
  • \n 換行主要是為了方便調(diào)試時顯示行號
  • gl_Position = vec4(0.0,0.0,0.0,1.0)

gl_Position 是 vertex shader 的內(nèi)置變量碉碉,必須賦值柴钻,表示圖形的坐標(biāo)
vec4 表示一個含有 4 個元素的 vector (向量)

  • gl_PointSize = 10.0

gl_PointSize 也是內(nèi)置變量,只有畫點(diǎn)時才有效垢粮。這里給了一個固定值贴届。

  • gl_FragColor = vec4(1.0,0.0,0.0,1.0)

gl_FragColor 是 fragment shader 的內(nèi)置變量,指定每個 fragment 的顏色蜡吧,這里給了一個紅色 (RGBA)

gl.drawArrays(gl.POINTS, 0, 1)

gl.drawArrays(mode, first, count)
mode 表示要畫圖形的類型毫蚓,可以是 gl.POINTS, gl.LINES,gl.TRIANGLES 等
first 從第幾個頂點(diǎn)開始畫
count 一共畫多少個頂點(diǎn)

這樣我們就畫了一個紅色的點(diǎn)昔善,但這個點(diǎn)的坐標(biāo)在哪里呢元潘?我們來看一下 webgl 坐標(biāo)系。

webgl coord.png

webgl 默認(rèn)是右手坐標(biāo)系君仆, z 軸從屏幕里面出來翩概,眼睛默認(rèn)在 (0, 0, 0) 位置往屏幕里面看。
什么是右手坐標(biāo)系返咱,自己用右手比劃一下就明白了

right hand.png

而 canvas 和 webgl 坐標(biāo)的映射關(guān)系是下面這樣的

canvas coord.png

canvas draw area 不一定是正方形的钥庇,一般都是長方形的。不論是正方形還是長方形咖摹,映射之后 canvas 左下角是 (-1.0,-1.0,-1.0) , 右上角是 (1.0,1.0,1.0)上沐,中間是 (0.0,0.0,0.0)
那是怎么映射過來的呢?后面講投影的時候就知道了楞艾。

我們點(diǎn)的坐標(biāo)是 vec4(0.0,0.0,0.0,1.0) 所以畫在了中間, 最后的 1.0 表示什么呢龄广?

這是一個 homogeneous coordinate (齊次坐標(biāo))硫眯。簡單來說 齊次坐標(biāo) (x, y, z, w) 等價于 3維坐標(biāo) (x/w, y/w, z/w)。當(dāng) w=1.0 時就可以用齊次坐標(biāo)來表示3維坐標(biāo)择同。
為什么要引入齊次坐標(biāo)两入,齊次坐標(biāo)有什么用?這個我理解的也不清楚敲才,也是比較迷惑的地方裹纳。
我的理解是 1. 當(dāng) w=0 時齊次坐標(biāo)可以表示無窮遠(yuǎn) 2. 矩陣變換都是用 4×4 的齊次坐標(biāo)來計算的

練習(xí):

  1. 試試改變點(diǎn)的位置 gl_Position = vec4(0.5, 0.0, 0.0, 1.0)
  2. 試試改變點(diǎn)的顏色

查看源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市紧武,隨后出現(xiàn)的幾起案子剃氧,更是在濱河造成了極大的恐慌,老刑警劉巖阻星,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朋鞍,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)滥酥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門更舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坎吻,你說我怎么就攤上這事缆蝉。” “怎么了瘦真?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵刊头,是天一觀的道長。 經(jīng)常有香客問我吗氏,道長芽偏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任弦讽,我火速辦了婚禮污尉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘往产。我一直安慰自己被碗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布仿村。 她就那樣靜靜地躺著锐朴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔼囊。 梳的紋絲不亂的頭發(fā)上焚志,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機(jī)與錄音畏鼓,去河邊找鬼酱酬。 笑死,一個胖子當(dāng)著我的面吹牛云矫,可吹牛的內(nèi)容都是我干的膳沽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼让禀,長吁一口氣:“原來是場噩夢啊……” “哼挑社!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起巡揍,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痛阻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吼肥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體录平,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡麻车,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了斗这。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片动猬。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖表箭,靈堂內(nèi)的尸體忽然破棺而出赁咙,到底是詐尸還是另有隱情,我是刑警寧澤免钻,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布彼水,位于F島的核電站,受9級特大地震影響极舔,放射性物質(zhì)發(fā)生泄漏凤覆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一拆魏、第九天 我趴在偏房一處隱蔽的房頂上張望盯桦。 院中可真熱鬧,春花似錦渤刃、人聲如沸拥峦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽略号。三九已至,卻和暖如春洋闽,著一層夾襖步出監(jiān)牢的瞬間玄柠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工诫舅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留随闪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓骚勘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撮奏。 傳聞我的和親對象是個殘疾皇子俏讹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

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