1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        WebGL 處理圖片,來(lái)玩

        共 6289字,需瀏覽 13分鐘

         ·

        2021-07-30 00:59

        “咕咕咕咕咕...”

        天空中傳來(lái)了鴿群的聲音,隨之而來(lái)的是悅耳的鴿哨兒??

        近日在 凹凸實(shí)驗(yàn)室 公眾號(hào)看到一篇文章《GLSL 著色器,來(lái)玩》,你們都來(lái)玩,那我走?

        玩笑話,其內(nèi)容還是有趣的,對(duì)于我這種 WebGL 青瓜蛋子還是十分友好;環(huán)境搭建直接使用了 ThreeJS,這樣就可以讓學(xué)習(xí)繁雜的 WebGL 基礎(chǔ) API 變成黑盒,讓新手更易學(xué)習(xí)。當(dāng)然如果想用原生 WebGL 標(biāo)準(zhǔn) API 來(lái)繪制,可以看我公眾號(hào)的 WebGL 專欄。

        既然那么好玩,索性跟風(fēng)也寫(xiě)個(gè)著色器的小文章來(lái)玩吧??

        小序

        何為著色器,簡(jiǎn)而言之即為自定義 GPU 處理圖形能力的程序,GLSL 基礎(chǔ)語(yǔ)法不在本文涉及范圍內(nèi),讀者可自行閱讀《GLSL ES 語(yǔ)法基礎(chǔ)》。

        既然要玩,也要玩點(diǎn)東西出來(lái),即要有點(diǎn)小成果;凹凸實(shí)驗(yàn)室 文章中以動(dòng)態(tài)渲染不同顏色為例,那本文就以渲染圖片為例,做一個(gè)可以切換圖片渲染效果的 Demo 吧(可以理解為切換濾鏡效果)。

        Demo 所涉及 GLSL 基礎(chǔ)知識(shí)并未超出《GLSL ES 語(yǔ)法基礎(chǔ)》一文之所及,僅應(yīng)用了些許數(shù)學(xué)方面的小知識(shí),從而實(shí)現(xiàn)圖片不同的渲染效果。

        環(huán)境搭建

        Demo 中并未使用任何第三方 WebGL 庫(kù),只使用了自己用 TypeScript 重寫(xiě)的 webgl-utils,整個(gè)項(xiàng)目框架使用了 webpack-react-template(兩個(gè)項(xiàng)目 GitHub 鏈接會(huì)附在文末)。要處理 GLSL 文件,故需對(duì) webpack.config.js 進(jìn)行些許修改:

        // webpack.config.js

        const config = {
          // ...
          module: {
            rules: [
              // ...
              {
                test/\.(svg|glsl)$/,
                issuer/\.(js|ts)x?$/,
                use: [
                  {
                    loader"raw-loader",
                  },
                ],
              },
            ],
          },
        };

        僅需讓 raw-loader 處理一下 GLSL 文件,順便在 types.d.ts 中加入對(duì) GLSL 文件的聲明即可:

        declare module '*.glsl' {
          export default '' as string;
        }

        程序主體

        整個(gè)程序主體由下面幾行代碼組成:

        const Main = ({ type }: Props) => {
        const canvas = useRef<HTMLCanvasElement>({} as HTMLCanvasElement);
        const [gl, setGL] = useState<WebGL2RenderingContext | null>(null);
        const [program, setProgram] = useState<WebGLProgram | null>(null);

        useEffect(() => {
        canvas.current.width = Math.floor(window.innerWidth * 0.6);
        canvas.current.height = Math.floor(window.innerHeight * 0.7);
        const ctx = canvas.current.getContext('webgl2');

        if (!ctx) {
        throw new Error('Failed to get WebGL2 context');
        }

        setGL(ctx);
        const p = createProgramFromSources(ctx, ShadersMap[type], [], []);
        setProgram(p);
        }, [type]);

        const animation = useCallback(() => {
        if (gl && program) {
        void render(gl, program, Image);
        }
        }, [gl, program]);

        requestAnimationFrame(() => {
        animation();
        });

        return (
        <canvas ref={canvas} id="canvas" />
        );
        };

        而最重要的 render 職責(zé)就是加載并渲染圖片,WebGL 如何加載圖片,大家可以閱讀 《WebGL 紋理映射》。紋理映射中選用了 超級(jí)賽亞人之神 圖片,這次就選 我妻善逸 ,渲染后效果如下圖:

        本次主要做了:交換紅藍(lán)通道、灰白、高斯模糊以及馬賽克四種效果,其中除了簡(jiǎn)單的數(shù)學(xué)知識(shí)外,還涉及到了一個(gè)重要的知識(shí)點(diǎn) 切換著色器。以下例子中頂點(diǎn)著色器都無(wú)需修改,只需對(duì)片元著色器進(jìn)行修改即可。

        原圖

        渲染原圖的頂點(diǎn)著色器和片元著色器很簡(jiǎn)單:

        // vertex-shader.glsl
        #version 300 es

        in vec2 a_Position;
        in vec2 a_TexCoord;
        uniform vec2 u_Resolution;
        out vec2 v_TexCoord;

        void main() {
        vec2 zeroToOne = a_Position / u_Resolution;
        vec2 zeroToTwo = zeroToOne * 2.0;
        vec2 clipSpace = zeroToTwo - 1.0;

        gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
        v_TexCoord = a_TexCoord;
        }

        上面有個(gè)小點(diǎn)需要注意,頂點(diǎn)著色器程序中將絕對(duì)的像素位置轉(zhuǎn)換到了 區(qū)間內(nèi),至于為何乘的為 而非 則取決于 gl.texParameteri 的設(shè)置。

        // fragment-shader.glsl
        #version 300 es

        precision highp float;

        uniform sampler2D u_Image;
        in vec2 v_TexCoord;
        out vec4 outColor;

        void main() {
        outColor = texture(u_Image, v_TexCoord);
        }

        切換紅藍(lán)通道

        片元著色器中的顏色是 rgba 形式的 vec4 變量,交換紅藍(lán)通道只需按如下變量賦值即可:

        // ...

        void main() {
        outColor = texture(u_Image, v_TexCoord).bgra;
        }

        渲染效果如下:

        著實(shí)有些瘆人 :(

        灰白圖

        將彩色圖片轉(zhuǎn)成灰白圖片,只需將 rgb 三個(gè)通道的顏色值進(jìn)行平均:

        // ...

        void main() {
        vec4 color = texture(u_Image, v_TexCoord);
        float average = (color.r + color.g + color.b) / 3.0;
        outColor = vec4(average, average, average, color.a);
        }

        高斯模糊

        高斯模糊(Gaussian Blur)是一種很常見(jiàn)的效果,通常用來(lái)降低噪聲或降低細(xì)節(jié)層次,我們可以在 PhotoShop 的專業(yè)繪圖軟件中見(jiàn)到其身影。

        它使用正態(tài)分布計(jì)算圖像中每個(gè)像素的變換,分布不為 0 的像素組成的卷積矩陣與原圖像做變換,每個(gè)像素值都是周圍元素的加權(quán)平均。因?yàn)閳D片是二維信息,所以要使用二維正態(tài)分布,如下圖:

        (圖片來(lái)源:https://images0.cnblogs.com/blog/502930/201309/11201048-3f10d0cc1d9d4d65a7326e467fc5bc11.jpg)

        原像素有最大的二維正態(tài)分布值,即有最大權(quán)重,故模糊后的像素最接近原像素,模糊后的整個(gè)圖像還能看出原圖像的影子。簡(jiǎn)單來(lái)說(shuō),高斯模糊的過(guò)程就是原圖像與二維正態(tài)分布做卷積,不再展開(kāi)講(因?yàn)槲乙膊粫?huì))??

        #version 300 es

        precision highp float;

        uniform sampler2D u_Image;
        uniform vec2 u_Resolution;
        in vec2 v_TexCoord;
        out vec4 outColor;

        // 每個(gè)像素的權(quán)重
        // 最中間的為原像素,權(quán)重最高
        float weight[9] = float[] (
        0.0947416, 0.118318, 0.0947416,
        0.118318, 0.147761, 0.118318,
        0.0947416, 0.118318, 0.0947416
        );

        void main() {
        vec4 color;

        for(int i = 0; i < 9; i++) {
        vec2 coord;
        coord.x = v_TexCoord.x + float(i % 3 - 1) / u_Resolution.x;
        coord.y = v_TexCoord.y + float(int(i / 3) - 1) / u_Resolution.y;
        color = color + texture(u_Image, coord) * weight[i];
        }

        outColor = color;
        }

        對(duì)原像素方圓 1 像素的值做加權(quán)平均,效果如下圖:

        仔細(xì)觀察還是可以發(fā)現(xiàn)差別的:

        馬賽克

        最后來(lái)講一個(gè)令人“厭惡”的效果 —— 馬賽克,好像任何東西打了碼之后就會(huì)變得很邪惡??

        實(shí)現(xiàn)馬賽克這個(gè)效果需引入另一個(gè)概念 —— 噪聲,很容易理解就是多余不必要的干擾信息。實(shí)現(xiàn)也很簡(jiǎn)單,我們只需生成一個(gè)“第三者”來(lái)“插足”原圖即可:

        #version 300 es

        precision highp float;

        uniform sampler2D u_Image;
        uniform vec2 u_Resolution;
        in vec2 v_TexCoord;
        out vec4 outColor;

        float random(vec2 st) {
        return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453);
        }

        void main() {
        vec2 st = v_TexCoord.xy / u_Resolution.xy * 20000.0;
        vec2 ipos = floor(st);
        vec3 color = vec3(random(ipos));
        vec4 tex = texture(u_Image, v_TexCoord);

        outColor = vec4(tex.rgb * 0.2 + color * 0.8, 1.0);
        }

        此處的“第三者”就是根據(jù)紋理像素坐標(biāo)生成的 color,然后根據(jù)相應(yīng)權(quán)重與原圖疊加在一起:

        其實(shí)真正的“打碼”并不是這種方式,打碼一般是將指定區(qū)域內(nèi)的內(nèi)容做加權(quán)平均,然后讓該區(qū)域內(nèi)的像素展示相同的顏色。比如我們以微信截圖的馬賽克為例:

        我們使用截圖工具對(duì) cdd(臭弟弟) 打碼后效果:

        會(huì)發(fā)現(xiàn)截圖工具每次將“步兵”轉(zhuǎn)成“騎兵”后,每個(gè)馬賽克像素大小的大小以及位置都是相同的,并且針對(duì)于同一個(gè)圖片打碼后的效果也都是相同的。這就是對(duì)圖片的特定大小區(qū)域內(nèi)的內(nèi)容做加權(quán)平均并設(shè)為相同顏色后的效果。這種效果各位可以自己嘗試實(shí)現(xiàn)。

        切換著色器??

        上面我們編寫(xiě)了五種不同的片元著色器,但如何在一個(gè)程序中使用這五種不同的著色器呢?

        回想一下在哪兒我們用到了 Shader?是不是在 gl.attachShader 的時(shí)候?所以當(dāng)我們想使用不同的 Shader 時(shí),我們直接使用新的 programattachShader 即可(記得要 useProgram),效果如下:

        篇末不點(diǎn)題

        WebGL 基礎(chǔ) API 知識(shí)量為 1,則其所涉及的其他領(lǐng)域知識(shí)(諸如圖形學(xué)、數(shù)學(xué)、物理等)可能為 100 甚至更多,唯有興趣趨勢(shì)才有激情和動(dòng)力前進(jìn)。

        最近項(xiàng)目較忙,故停更一陣,畢竟生活、工作才是重心;后續(xù)會(huì)繼如以往,分享感興趣、有趣、有用的內(nèi)容,但至于面試等相關(guān)文章,俯拾即是,不寫(xiě)也罷。


        課外輔導(dǎo)鏈接??

        知識(shí)點(diǎn)學(xué)習(xí)??

        1. 高斯模糊:https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%A8%A1%E7%B3%8A
        2. 正態(tài)分布:https://zh.wikipedia.org/wiki/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83
        3. 卷積:https://zh.wikipedia.org/wiki/%E5%8D%B7%E7%A7%AF

        GitHub Repo

        1. webgl-utils-ts:https://github.com/LiJiahaoCoder/webgl-utils-ts
        2. webpack-react-template:https://github.com/LiJiahaoCoder/webpack-react-template
        3. 本文示例源碼:https://github.com/LiJiahaoCoder/webgl-process-image
        瀏覽 118
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲V一| 97久久爽无码人妻AⅤ精品牛牛 | 免费做爱视频网站 | 国产又黄又 | 国产中文| 亚洲无码中文字幕强奸乱伦 | 91人妻爽爽 | 麻豆91入口 | 亚欧精品久久久久久久久久 | 又粗又长又爽视频 |