webgl 系列 —— 繪製貓

来源:https://www.cnblogs.com/pengjiali/archive/2023/03/20/17237478.html
-Advertisement-
Play Games

其他章節請看: webgl 系列 繪製貓 上文我們瞭解瞭如何繪製漸變彩色三角形,明白了圖形裝配、光柵化,以及片元著色器計算片元的顏色。 現在如果讓你繪製如下一隻貓。難道繪製很多三角形,然後指定它們的顏色?那樣簡直太難、太繁瑣了。 這時可以使用三維圖形學中的紋理映射技術來解決這個問題。 紋理映射簡單來 ...


其他章節請看:

webgl 系列

繪製貓

上文我們瞭解瞭如何繪製漸變彩色三角形,明白了圖形裝配光柵化,以及片元著色器計算片元的顏色。

現在如果讓你繪製如下一隻貓。難道繪製很多三角形,然後指定它們的顏色?那樣簡直太難、太繁瑣了。

這時可以使用三維圖形學中的紋理映射技術來解決這個問題。

紋理映射簡單來講就是將一張圖映射(貼)到一個幾何圖形的錶面。

例如這樣:

本篇最後將實現如下效果:

漸變矩形

根據漸變三角形,我們很容易就可以繪製一個漸變矩形。就像這樣:

完整代碼如下:

const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec2 a_uv;
varying vec2 v_uv;
void main() {
  gl_Position = a_Position;
  v_uv = a_uv;
}
`
const FSHADER_SOURCE = `
precision mediump float;
varying vec2 v_uv;
void main() {
  gl_FragColor = vec4(v_uv, 0.0, 1.0);
}
`

function main() {
    const canvas = document.getElementById('webgl');
    const gl = canvas.getContext("webgl");
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    gl.clearColor(0.0, 0.5, 0.5, 1.0);


    // 幾何圖形的4個頂點的坐標
    const positions = new Float32Array([
        // 左下角是第一個點,逆時針
        -0.5, -0.5,
        0.5, -0.5,
        0.5, 0.5,
        -0.5, 0.5,
    ])

    // 紋理的4個點的坐標。通常稱為 uv(u類似x,v類似y) 坐標
    const uvs = new Float32Array([
        // 左下角是第一個點,逆時針,與頂點坐標保持對應
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0
    ])

    initVertexBuffers(gl, positions)

    initUvBuffers(gl, uvs)

    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);

}

function initVertexBuffers(gl, positions) {
    const vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
        console.log('創建緩衝區對象失敗');
        return -1;
    }

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

    const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
    }

    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(a_Position);
}

function initUvBuffers(gl, uvs) {
    const uvsBuffer = gl.createBuffer();
    if (!uvsBuffer) {
        console.log('創建 uvs 緩衝區對象失敗');
        return -1;
    }
    gl.bindBuffer(gl.ARRAY_BUFFER, uvsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, uvs, gl.STATIC_DRAW);
    const a_uv = gl.getAttribLocation(gl.program, 'a_uv');
    if (a_uv < 0) {
        console.log('Failed to get the storage location of a_uv');
        return -1;
    }

    gl.vertexAttribPointer(a_uv, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(a_uv);
}

漸變矩形從左下角,逆時針,依次是黑、紅、黃、綠。與這段代碼是匹配的:

// 幾何圖形的4個頂點的坐標
const positions = new Float32Array([
    // 左下角是第一個點,逆時針
    -0.5, -0.5,
    0.5, -0.5,
    0.5, 0.5,
    -0.5, 0.5,
])

const uvs = new Float32Array([
    // 左下角是第一個點,逆時針,與頂點坐標保持對應
    0.0, 0.0, // 黑
    1.0, 0.0, // 紅
    1.0, 1.0, // 黃
    0.0, 1.0, // 綠
])

這裡的 uvs 涉及紋理(貼圖)坐標,是為貼圖做準備。

Tip: 接下來只需要把矩形中每個像素的顏色換成紋理對應像素的顏色即可。

紋理坐標

對於貼圖,幾何圖形就得獲取紋理對應像素的顏色,得有一個映射關係,否則獲取哪個像素的顏色。坐標對應關係如下:

紋理坐標如下:

// 左下角,逆時針
0.0 0.0 // 左下角
1.0 0.0 // 右下角
1.0 1.0 // 右上角
0.0 1.0 // 左上角

漸變矩形我們所做的工作就是將紋理的範圍和幾何圖形對應上。

為了區分其他坐標,這裡紋理坐標不叫 (x, y),通常叫 (u, v) 或 (s, t)。

Tip:照片尺寸和紋理坐標是沒有關係的。無論圖片多大,右下角都是(1.0, 0.0)。假如一張 1024*256 的圖片放入 256*256 的幾何圖形中,貼圖的寬度就會被壓縮。就像這樣:

繪製貓

效果

思路

  • 通過 new Image 定義圖片,圖片載入完成後創建紋理
  • 紋理的使用類似緩衝對象,有一系列規則
  • 在將紋理傳給片元著色器中定義的取樣器 u_Sampler(好像圖片的句柄)
  • 最後通過 texture2D(u_Sampler, v_uv) 取得紋理像素的顏色

完整代碼

const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec2 a_uv;
varying vec2 v_uv;
void main() {
  gl_Position = a_Position;
  v_uv = a_uv;
}
`
const FSHADER_SOURCE = `
precision mediump float;
// 定義一個取樣器。sampler2D 是一種數據類型,就像 vec2
uniform sampler2D u_Sampler;
varying vec2 v_uv;
void main() {
  // texture2D(sampler2D sampler, vec2 coord) - 著色器語言內置函數,從 sampler 指定的紋理上獲取 coord 指定的紋理坐標處的像素
  vec4 color = texture2D(u_Sampler, v_uv);
  gl_FragColor = color;
}
`

function main() {
    const canvas = document.getElementById('webgl');
    const gl = canvas.getContext("webgl");
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    gl.clearColor(0.0, 0.5, 0.5, 1.0);

    // 幾何圖形的4個頂點的坐標
    const verticesOfPosition = new Float32Array([
        // 左下角是第一個點,逆時針
        -0.5, -0.5,
        0.5, -0.5,
        0.5, 0.5,
        -0.5, 0.5,
    ])

    // 紋理的4個點的坐標
    const uvs = new Float32Array([
        // 左下角是第一個點,逆時針,與頂點坐標保持對應
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0
    ])

    // 和漸變矩形相同
    initVertexBuffers(gl, verticesOfPosition)

    // 和漸變矩形相同
    initUvBuffers(gl, uvs)

    initTextures(gl)
}

// 初始化紋理。之所以為複數 s 是因為可以貼多張圖片。
function initTextures(gl) {
    // 定義圖片
    const img = new Image();
    // 請求 CORS 許可。解決圖片跨域問題
    img.crossOrigin = "";
    // The image element contains cross-origin data, and may not be loaded.
    img.src = "http://placekitten.com/256/256";

    img.onload = () => {
        // 創建紋理
        const texture = gl.createTexture();

        // 取得取樣器
        const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
        if (!u_Sampler) {
            console.log('Failed to get the storage location of u_Sampler');
            return false;
        }
        // pixelStorei - 圖像預處理:圖片上下對稱翻轉坐標軸 (圖片本身不變)
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        // 激活紋理單元
        gl.activeTexture(gl.TEXTURE0);
        // 綁定紋理對象
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 配置紋理參數
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        // 紋理圖片分配給紋理對象
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
        // 將紋理單元傳給片元著色器
        gl.uniform1i(u_Sampler, 0);

        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
    }
}

Tip: 為了方便演示,這裡通過 http://placekitten.com/256/256 返回一個指定尺寸貓(256*256)的圖片。需要解決圖片跨域問題,詳情請看這裡

圖像 Y 軸反轉

pixelStorei - 用於圖像預處理的函數

假如註釋 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 圖片就會反過來。就像這樣:

原因是 canvas 坐標中的 y 是向下,而紋理的 y(v) 是向上:

激活紋理單元

webgl 通過紋理單元的機制同時使用多個紋理。每個紋理單元有個編號來管理一張紋理圖片。

根據硬體和瀏覽器對webgl的實現,webgl 至少支持8個紋理單元,有的更多。

內置變數 gl.TEXTURE0gl.TEXTURE1...gl.TEXTURE7 各表示一個紋理單元

activeTexture - 用來激活指定的紋理單元。例如激活一個紋理單元:

綁定紋理對象

gl.bindTexture(target, texture) - 指定紋理對象類型,將其綁定到紋理單元。就像這樣:

target 指紋理對象的類型(我們這裡就使用二維紋理):

  • gl.TEXTURE_2D: 二維紋理
  • gl.TEXTURE_CUBE_MAP: 立方體映射紋理

在 webgl 中不能直接操作紋理對象,必須將其綁定到紋理單元上,在通過紋理單元來操作。

圖片分配給紋理對象

執行完 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img) 後,圖片將分配給紋理對象。就像這樣:

這行代碼參數很多,最主要的就是最後一個參數,即圖片。

Tip:texImage2D 語法如下:

gl.texImage2D(target, level, internalformat, format, type, HTMLImageElement? pixels):
    - target - gl.TEXTURE_2D `二維紋理` 或 gl.TEXTURE_CUBE_MAP 立方體映射紋理
    - level - 傳入 0(該參數為金字塔紋理準備,這裡不是)
    - internalformat - 圖像的內部格式,這裡是 RBG
    - format - 紋理的數據格式,必須與 internalformat 相同
    - type - 紋理數據類型
    - HTMLImageElement - 圖片

紋理單元傳給片元著色器

前面已經將貼圖放入紋理對象,執行 gl.uniform1i(u_Sampler, 0) 就會將紋理單元傳給片元著色器。效果如下:

設置紋理參數

gl.texParameteri 用於設置紋理參數

語法:

gl.texParameterf(GLenum target, GLenum pname, GLfloat param)

target
    gl.TEXTURE_2D: 二維紋理。
    gl.TEXTURE_CUBE_MAP: 立方體紋理。

pname
    gl.TEXTURE_MAG_FILTER 紋理放大濾波器    gl.LINEAR (預設值), gl.NEAREST.
    gl.TEXTURE_MIN_FILTER 紋理縮小濾波器 
    gl.TEXTURE_WRAP_S     紋理坐標水平填充  gl.REPEAT (預設值),gl.CLAMP_TO_EDGE, gl.MIRRORED_REPEAT.
    gl.TEXTURE_WRAP_T     紋理坐標垂直填充  gl.REPEAT (預設值),gl.CLAMP_TO_EDGE, gl.MIRRORED_REPEAT.

在繪製貓時我們進行瞭如下設置:

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

我們的貼圖是二維的,所以選用 TEXTURE_2D。

TEXTURE_MAG_FILTER 放大紋理。例如將尺寸 1616 的圖片貼到 3232 的幾何圖形上,就得無中生有。無中生有,LINEAR 表示距離新像素最近的4個像素顏色的加權平均,比 NEAREST(最近的) 運算量大,但質量更好

只貼部分

需求:將圖片貼到幾何圖形左下角部分。

可以通過放大紋理坐標。就像這樣:

修改代碼如下:

// 將 1.0 統統變成 2.0,就好像圖片變小了一倍
const uvs = new Float32Array([
    0.0, 0.0,
    2.0, 0.0,
    2.0, 2.0,
    0.0, 2.0
])

效果確是這樣:

這是因為 TEXTURE_WRAP_S 和 TEXTURE_WRAP_T 預設值是 REPEAT。

增加如下代碼:

// 水平方向 CLAMP_TO_EDGE 重覆邊緣那條線的像素
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// 垂直方向 MIRRORED_REPEAT 反光鏡重覆
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);

效果如下:

多幅紋理

這裡我們實現多幅紋理的效果。首先準備一張 256*256 的圖片,就像畫貓一樣,這裡先顯示第二張紋理:

const FSHADER_SOURCE = `
precision mediump float;
uniform sampler2D u_Sampler;
uniform sampler2D u_Sampler2;
varying vec2 v_uv;
void main() {
  vec4 color = texture2D(u_Sampler, v_uv);
  vec4 color2 = texture2D(u_Sampler2, v_uv);
  // 只顯示第二張貼圖
  gl_FragColor = color2;
}
`

function main() {
    // ...

    // 紋理的4個點的坐標
    const uvs = new Float32Array([
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0
    ])
    // 不變
    initVertexBuffers(gl, verticesOfPosition)
    // 不變
    initUvBuffers(gl, uvs)
    // 不變
    initTextures(gl)

    initMaskTextures(gl)
}

// 初始化蒙版紋理
function initMaskTextures(gl) {
    const img = new Image();
    img.src = "./mask.png";

    img.onload = () => {
        const texture = gl.createTexture();

        const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler2');
        if (!u_Sampler) {
            console.log('Failed to get the storage location of u_Sampler');
            return false;
        }
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        // 第二個紋理單元
        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
        // 第二個紋理單元
        gl.uniform1i(u_Sampler, 1);

        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
    }
}

效果如下:

:假如將 mask.png 從 256256 改成 400400 ,圖片將不能顯示。因為WebGL限制了紋理的維度必須是2的整數次冪, 2 的冪有 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 等等。更多細節請看這裡

接下來顯示多幅紋理,主要涉及向量間的運算。修改如下代碼:

// 左圖
// 向量相乘,(0,0,0) 是黑色,其他值和黑色相乘則是黑色,所中間還是黑色
gl_FragColor = color * color2;

// 右圖
// `(vec4(1, 1, 1, 2) - color2)` 相當於取反
gl_FragColor = color * (vec4(1, 1, 1, 2) - color2);

效果如下:

完整代碼

const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec2 a_uv;
varying vec2 v_uv;
void main() {
  gl_Position = a_Position;
  v_uv = a_uv;
}
`
const FSHADER_SOURCE = `
precision mediump float;
// 定義一個取樣器。sampler2D 是一種數據類型,就像 vec2
uniform sampler2D u_Sampler;
uniform sampler2D u_Sampler2;
varying vec2 v_uv;
void main() {
  // texture2D(sampler2D sampler, vec2 coord) - 著色器語言內置函數,從 sampler 指定的紋理上獲取 coord 指定的紋理坐標處的像素
  vec4 color = texture2D(u_Sampler, v_uv);
  vec4 color2 = texture2D(u_Sampler2, v_uv);
  gl_FragColor = color * color2;
}
`

function main() {
    const canvas = document.getElementById('webgl');
    const gl = canvas.getContext("webgl");
    if (!gl) {
        console.log('Failed to get the rendering context for WebGL');
        return;
    }

    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
        console.log('Failed to intialize shaders.');
        return;
    }

    gl.clearColor(0.0, 0.5, 0.5, 1.0);

    // 幾何圖形的4個頂點的坐標
    const verticesOfPosition = new Float32Array([
        // 左下角是第一個點,逆時針
        -0.5, -0.5,
        0.5, -0.5,
        0.5, 0.5,
        -0.5, 0.5,
    ])

    // 紋理的4個點的坐標
    const uvs = new Float32Array([
        // 左下角是第一個點,逆時針,與頂點坐標保持對應
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0
    ])

    initVertexBuffers(gl, verticesOfPosition)

    initUvBuffers(gl, uvs)

    initTextures(gl)
    initMaskTextures(gl)

}

// 初始化紋理。之所以為複數 s 是因為可以貼多張圖片。
function initTextures(gl) {
    // 定義圖片
    const img = new Image();
    // 請求 CORS 許可。解決圖片跨域問題
    img.crossOrigin = "";
    // The image element contains cross-origin data, and may not be loaded.
    img.src = "http://placekitten.com/256/256";

    img.onload = () => {
        // 創建紋理
        const texture = gl.createTexture();

        // 取得取樣器
        const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
        if (!u_Sampler) {
            console.log('Failed to get the storage location of u_Sampler');
            return false;
        }
        // pixelStorei - 圖像預處理:圖片上下對稱翻轉坐標軸 (圖片本身不變)
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        // 激活紋理單元
        gl.activeTexture(gl.TEXTURE0);
        // 綁定紋理對象
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 配置紋理參數
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
        // 紋理圖片分配給紋理對象
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
        // 將紋理單元傳給片元著色器
        gl.uniform1i(u_Sampler, 0);

        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
    }
}

// 初始化紋理。之所以為複數 s 是因為可以貼多張圖片。
function initMaskTextures(gl) {
    const img = new Image();
    img.src = "./mask.png";
    // img.src = "./mask400_400.png";

    img.onload = () => {
        // 創建紋理
        const texture = gl.createTexture();

        // 取得取樣器
        const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler2');
        if (!u_Sampler) {
            console.log('Failed to get the storage location of u_Sampler');
            return false;
        }
        // pixelStorei - 圖像預處理:圖片上下對稱翻轉坐標軸 (圖片本身不變)
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        // 激活紋理單元
        gl.activeTexture(gl.TEXTURE1);
        // 綁定紋理對象
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 配置紋理參數
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        // 紋理圖片分配給紋理對象
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, img);
        // 將紋理單元傳給片元著色器
        gl.uniform1i(u_Sampler, 1);

        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
    }
}

function initVertexBuffers(gl, positions) {
    const vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
        console.log('創建緩衝區對象失敗');
        return -1;
    }

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

    const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    if (a_Position < 0) {
        console.log('Failed to get the storage location of a_Position');
        return -1;
    }

    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(a_Position);
}

function initUvBuffers(gl, uvs) {
    const uvsBuffer = gl.createBuffer();
    if (!uvsBuffer) {
        console.log('創建 uvs 緩衝區對象失敗');
        return -1;
    }
    gl.bindBuffer(gl.ARRAY_BUFFER, uvsBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, uvs, gl.STATIC_DRAW);
    const a_uv = gl.getAttribLocation(gl.program, 'a_uv');
    if (a_uv < 0) {
        console.log('Failed to get the storage location of a_uv');
        return -1;
    }

    gl.vertexAttribPointer(a_uv, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(a_uv);
}

其他章節請看:

webgl 系列

作者:彭加李
出處:https://www.cnblogs.com/pengjiali/p/17237478.html
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • linux伺服器預設通過22埠用ssh協議登錄,這種不安全。今天想做限制,即允許部分來源ip連接伺服器。 案例目標:通過iptables規則限制對linux伺服器的登錄。 處理方法:編寫為sh腳本,以便多次執行。iptables.sh : iptables -I INPUT -p tcp --dp ...
  • 一、單表查詢 庫表student.report,有3個欄位, 姓名、 學科、 成績, 記錄如下, 根據要求完成SQL語句 | Name | Subject | Result | | | | | | 李白 | Math | 95 | | 杜甫 | English | 83 | | 李商隱 | Math ...
  • 資料庫安裝方式:通用二進位安裝 策略1:直接拷貝資料庫文件 步驟1:主伺服器上停用資料庫 [root@node01 ~]# systemctl stop mysqld.service 步驟2:進入數據目錄,打包並壓縮數據文件 [root@node01 ~]# cd /usr/local/mysql/ ...
  • MySQL資料庫的安裝 (如果安裝失敗請看鏈接https://www.cnblogs.com/seeyouone/p/17236660.html) 註意: 必須用系統管理員身份運行mysql安裝程式。 安裝目錄切記不要用中文。 步驟一:雙擊mysql8的安裝嚮導 步驟二:安裝 (1)如果是首次安裝m ...
  • 步驟一:軟體的卸載準備 學習網路編程時,TCP/IP協議程式有伺服器端和客戶端。mysql這個資料庫管理軟體是使用TCP/IP協議。我們現在要卸載的是mysql的伺服器端,它沒有界面。 【計算】-->右鍵-->【管理】-->【服務】-->【mysql的服務】-->【停止】 步驟二:軟體的卸載 方式一 ...
  • 重要的話先說,今晚(3月20日)19:30 gt-checksum新版本發佈會,點擊下麵的鏈接預約: 會議詳情 (tencent.com) gt-checksum 1.2.0版本發佈後,受到了廣大社區用戶的熱烈響應。短短幾天,便有幾十個star,以及社區用戶提交了十幾條issue反饋問題,更有熱心用 ...
  • 前言 在initEvents中發現的有意思的東西,就是 Vue 針對 Error 的處理,說實話之前壓根沒在意過 Vue 是如何收集處理 Error 的; errorHandler:https://v2.cn.vuejs.org/v2/api#errorHandler ?> 從 2.2.0 起,這個 ...
  • (目錄) Ajax 工作原理 Ajax Ajax 是前後端非同步交互的工具,非同步更新,ajax 包含 XMLHttpRequests 對象(非同步地與伺服器交換數據,AJAX 核心) JavaScript/DOM(信息顯示/交互) CSS(給數據定義樣式) XML 或 JSON(作為轉換數據的格式) 工 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...