亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

webgltilemap渲染不正確的UV計算

webgltilemap渲染不正確的UV計算

千萬里不及你 2023-09-07 16:01:32
我正在嘗試將圖塊地圖渲染到單個四邊形上。我的方法使用“圖塊地圖”紋理,其中每個像素存儲圖塊集中的圖塊的 X 和 Y 索引。渲染片段時,想法是:使用頂點紋理坐標對“平鋪貼圖”紋理進行采樣從紋理的 R 和 G 通道檢索 X 和 Y 索引計算所選圖塊的 UV使用 UV 對紋理圖集進行采樣我在讓 #3 工作時遇到問題。這是我嘗試用來渲染它的著色器代碼:頂點#version 300 esprecision mediump float;uniform mat4 uVIEW;uniform mat4 uPROJECTION;uniform mat3 uMODEL;layout(location = 0) in vec2 aPOSITION;layout(location = 1) in vec2 aTEXCOORD;out vec2 vTEXCOORD;void main(){    // flip uv and pass it to fragment shader    vTEXCOORD = vec2(aTEXCOORD.x, 1.0f - aTEXCOORD.y);    // transform vertex position    vec3 transformed = uMODEL * vec3(aPOSITION, 1.0);    gl_Position = uPROJECTION * uVIEW * vec4(transformed.xy, 0.0, 1.0);}分段#version 300 esprecision mediump float;precision mediump usampler2D;uniform usampler2D uMAP;uniform sampler2D uATLAS;uniform vec2 uATLAS_SIZE;in vec2 vTEXCOORD;out vec4 oFRAG;void main(){    // sample "tile map" texture    vec4 data = vec4(texture(uMAP, vTEXCOORD));    // calculate UV    vec2 uv = (data.xy * 32.0 / uATLAS_SIZE) + (vTEXCOORD * 32.0 / uATLAS_SIZE);    // sample the tileset    oFRAG = texture(uATLAS, uv);}我相信這是罪魁禍首:vec2 uv = (data.xy * 32.0 / uATLAS_SIZE) + (vTEXCOORD * 32.0 / uATLAS_SIZE);這里的公式是uv = (tile_xy_indices * tile_size) + (texcoord * tile_size),其中:texcoord是頂點uv(標準[0, 1], [0, 0], [1, 0], [1, 1])tile_xy_indices是圖塊集中圖塊的 X、Y 坐標tile_size是圖塊集中一個圖塊的標準化尺寸因此,如果我有值texcoord = (0, 0), tile_xy_indices = (7, 7), tile_size = (32 / 1024, 32 / 1024),那么該片段的 UV 應該是(0.21875, 0.21875),如果texcoord = (1, 1),那么它應該是(0.25, 0.25)。這些值對我來說似乎是正確的,為什么它們會產生錯誤的結果,以及如何修復它?
查看完整描述

1 回答

?
米琪卡哇伊

TA貢獻1998條經驗 獲得超6個贊

代碼將這兩行中的三件事混為一談


// sample "tile map" texture

vec4 data = vec4(texture(uMAP, vTEXCOORD));

// calculate UV

vec2 uv = (data.xy * 32.0 / uATLAS_SIZE) + (vTEXCOORD * 32.0 / uATLAS_SIZE);

// sample the tileset

第一個,對于您正在繪制的整個四邊形,遍歷整個圖塊地圖。這可能不是你想要的。通常使用圖塊地圖的應用程序希望顯示其中的一部分,而不是整個內容。


第二個問題是第二行需要知道一個圖塊將覆蓋多少個像素,而不是一個圖塊有多少個像素。換句話說,如果您有一個 32x32 的圖塊并以 4x4 像素繪制它,那么您的紋理坐標需要在該圖塊上以 4 像素(而不是 32 像素)從 0.0 到 1.0。


第三個問題是除以 32 不會穿過 32 像素圖塊,除非紋理上有 32 個圖塊。假設您有一個 32x32 像素的圖塊,但圖塊集中有 8x4 的圖塊。你需要從 0 到 1 穿過 1/8 和 1/4,而不是穿過 1/32


實際上它使用了 2 個矩陣。一個是繪制四邊形,四邊形可以旋轉、縮放、以 3D 方式投影等,但我們可以說,對于圖塊地圖來說,正常情況就是只繪制一個覆蓋畫布的四邊形。


第二個是紋理矩陣(或圖塊矩陣),其中每個單元是 1 個圖塊。因此,給定一個 0 到 1 的四邊形,您可以計算一個矩陣來將該四邊形展開并旋轉到上面的四邊形。


假設您不旋轉,您仍然需要決定在四邊形上和下繪制多少塊。如果您希望四邊形上有 4 個圖塊,向下有 3 個圖塊,那么您可以將比例設置為 x=4 和 y=3。


這樣,每個圖塊都會在自己的空間中自動從 0 變為 1?;蛘邠Q句話說,圖塊 2x7 從 U 中的 2.0<->3.0 和 V 中的 7.0<->8.0 開始。然后我們可以從地圖圖塊 2,7 中查找并使用該圖塊覆蓋以下空間中的fract圖塊:瓷磚占據四邊形。

const vs = `#version 300 es

precision mediump float;


uniform mat4 uVIEW;

uniform mat4 uPROJECTION;

uniform mat4 uMODEL;


uniform mat4 uTEXMATRIX;


layout(location = 0) in vec4 aPOSITION;

layout(location = 1) in vec4 aTEXCOORD;


out vec2 vTEXCOORD;


void main()

{

? ? vTEXCOORD = (uTEXMATRIX * aTEXCOORD).xy;

? ? gl_Position = uPROJECTION * uVIEW * uMODEL * aPOSITION;

}

`;


const fs = `#version 300 es

precision mediump float;

precision mediump usampler2D;


uniform usampler2D uMAP;

uniform sampler2D uATLAS;

uniform vec2 uTILESET_SIZE; // how many tiles across and down the tileset


in vec2 vTEXCOORD;


out vec4 oFRAG;


void main()

{

? ? // the integer portion of vTEXCOORD is the tilemap coord

? ? ivec2 mapCoord = ivec2(vTEXCOORD);

? ? uvec4 data = texelFetch(uMAP, mapCoord, 0);

? ??

? ? // the fractional portion of vTEXCOORD is the UV across the tile

? ? vec2 texcoord = fract(vTEXCOORD);


? ? vec2 uv = (vec2(data.xy) + texcoord) / uTILESET_SIZE;

? ??

? ? // sample the tileset

? ? oFRAG = texture(uATLAS, uv);

}

`;


const tileWidth = 32;

const tileHeight = 32;

const tilesAcross = 8;

const tilesDown = 4;


const m4 = twgl.m4;

const gl = document.querySelector('canvas').getContext('webgl2');

if (!gl) alert('need WebGL2');


// compile shaders, link, look up locations

const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// gl.createBuffer, bindBuffer, bufferData

const bufferInfo = twgl.createBufferInfoFromArrays(gl, {

? aPOSITION: {

? ? numComponents: 2,

? ? data: [

? ? ? 0, 0,

? ? ? 1, 0,

? ? ? 0, 1,

? ? ??

? ? ? 0, 1,

? ? ? 1, 0,

? ? ? 1, 1,

? ? ],

? },

? aTEXCOORD: {

? ? numComponents: 2,

? ? data: [

? ? ? 0, 0,

? ? ? 1, 0,

? ? ? 0, 1,

? ? ??

? ? ? 0, 1,

? ? ? 1, 0,

? ? ? 1, 1,

? ? ],

? },

});


function r(min, max) {

? if (max === undefined) {

? ? max = min;

? ? min = 0;

? }

? return min + (max - min) * Math.random();

}


// make some tiles

const ctx = document.createElement('canvas').getContext('2d');

ctx.canvas.width = tileWidth * tilesAcross;

ctx.canvas.height = tileHeight * tilesDown;

ctx.font = "bold 24px sans-serif";

ctx.textAlign = "center";

ctx.textBaseline = "middle";


const f = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~';

for (let y = 0; y < tilesDown; ++y) {

? for (let x = 0; x < tilesAcross; ++x) {

? ? const color = `hsl(${r(360) | 0},${r(50,100)}%,50%)`;

? ? ctx.fillStyle = color;

? ? const tx = x * tileWidth;

? ? const ty = y * tileHeight;

? ? ctx.fillRect(tx, ty, tileWidth, tileHeight);

? ? ctx.fillStyle = "#FFF";

? ? ctx.fillText(f.substr(y * 8 + x, 1), tx + tileWidth * .5, ty + tileHeight * .5);?

? }

}

document.body.appendChild(ctx.canvas);


const tileTexture = twgl.createTexture(gl, {

?src: ctx.canvas,

?minMag: gl.NEAREST,

});


// make a tilemap

const mapWidth = 400;

const mapHeight = 300;

const tilemap = new Uint32Array(mapWidth * mapHeight);

const tilemapU8 = new Uint8Array(tilemap.buffer);

const totalTiles = tilesAcross * tilesDown;

for (let i = 0; i < tilemap.length; ++i) {

? const off = i * 4;

? // mostly tile 9

? const tileId = r(4) < 1?

? ? ? ? (r(totalTiles) | 0)

? ? ? : 9;

? tilemapU8[off + 0] = tileId % tilesAcross;

? tilemapU8[off + 1] = tileId / tilesAcross | 0;

}


const mapTexture = twgl.createTexture(gl, {

? internalFormat: gl.RGBA8UI,

? src: tilemapU8,

? width: mapWidth,

? minMag: gl.NEAREST,

});


function ease(t) {

? return Math.cos(t) * .5 + .5;

}


function lerp(a, b, t) {

? return a + (b - a) * t;

}


function easeLerp(a, b, t) {

? return lerp(a, b, ease(t));

}


function render(time) {

? time *= 0.001;? // convert to seconds;

??

? gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

? gl.clearColor(0, 1, 0, 1);

? gl.clear(gl.COLOR_BUFFER_BIT);

??

? gl.useProgram(programInfo.program);

? twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);??


? // these mats affects where the quad is drawn

? const projection = m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);

? const view = m4.identity();

? const model =

? m4.scaling([gl.canvas.width, gl.canvas.height, 1]);

?

? const tilesAcrossQuad = 10;//easeLerp(.5, 2, time * 1.1);

? const tilesDownQuad = 5;//easeLerp(.5, 2, time * 1.1);

??

? // scroll position in tiles

? // set this to 0,0 and the top left corner of the quad

? // will be the start of the map.

? const scrollX = time % mapWidth;

? const scrollY = 0;//time % (mapHeight * tileHeight);

??

? const tmat = m4.identity();

? // sets where in the map to look at in tile coordinates

? // so 3,4 means start drawing 3 tiles over, 4 tiles down

? m4.translate(tmat, [scrollX, scrollY, 0], tmat);

? // sets how many tiles to display

? m4.scale(tmat, [tilesAcrossQuad, tilesDownQuad, 1], tmat);


? twgl.setUniforms(programInfo, {

? ? uPROJECTION: projection,

? ? uVIEW: view,

? ? uMODEL: model,

? ? uTEXMATRIX: tmat,

? ? uMAP: mapTexture,

? ? uATLAS: tileTexture,

? ? uTILESET_SIZE: [tilesAcross, tilesDown],

? });

??

? gl.drawArrays(gl.TRIANGLES, 0, 6);

??

? requestAnimationFrame(render);

}

requestAnimationFrame(render);

canvas { border: 1px solid black; }

<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

<canvas></canvas>

下次發布工作片段對回答者來說會更加友好。


查看完整回答
反對 回復 2023-09-07
  • 1 回答
  • 0 關注
  • 94 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號