![](/img/trans.png)
[英]Three.js - depthWrite vs depthTest for transparent canvas texture map on THREE.Points
[英]Transparent drawing on canvas-sourced texture in THREE.js
我在THREE.js應用程序中通過創建PlaneGeometry
/ BasicMaterial
網格並使用畫布支持其紋理來創建2d曲面:
this.canvas = makeCanvas(canvasWidth, canvasHeight);
this.ctx = this.canvas.getContext('2d');
this.texture = new THREE.Texture(this.canvas);
this.texture.minFilter = THREE.LinearFilter;
this.texture.magFilter = THREE.LinearFilter;
this.texture.anisotropy = 16;
this.mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), new THREE.MeshBasicMaterial({
map: this.texture,
overdraw: true,
side: THREE.DoubleSide,
transparent: true
}));
正常工作-除非我想透明繪制。 然后,我需要創建另一個紋理作為alphaMap
進行綁定,並在兩個畫布上下文之間復制所有繪制操作。 性能很好,但是代碼看起來絕對可怕:
var circleAlpha = 'rgb(100, 100, 100);',
segmentAlpha = 'rgb(200, 200, 200);';
ctx.fillStyle = 'rgb(0, 255, 255);';
if (this.segment === -1) {
circleAlpha = segmentAlpha;
}
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.alphaCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
var drawArc = function (c, w, h, piece) {
var angw = 2 * Math.PI / 6;
c.beginPath();
c.arc(w / 2, h / 2, 512, angw * piece, angw * (piece + 1), false);
c.arc(w / 2, h / 2, 300, angw * (piece + 1), angw * piece, true);
c.closePath();
c.fill();
};
for (var i = 0; i < 6; i++) {
this.alphaCtx.fillStyle = i == this.segment ? segmentAlpha : circleAlpha;
drawArc(ctx, this.canvas.width, this.canvas.height, i);
drawArc(this.alphaCtx, this.canvas.width, this.canvas.height, i);
}
this.updateTexture();
this.alphaTexture.needsUpdate = true;
我一直在計划編寫一個小的實用程序庫來自動處理此問題,但是在我這樣做之前,我想知道是否我只是愚蠢的人,並且有一種更簡單的方法可以做到這一點。
您可以編寫一個split函數,而不是渲染到兩個畫布。 這種方法將允許您按預期使用Alpha通道對一個畫布發出多個繪制操作。
完成后,只需在拆分器上運行它即可,該拆分器將返回兩個畫布,一個畫布用於顏色,一個灰度用於Alpha通道。
(您當然可以將主色用於顏色,只要注意alpha通道將消失)。
var ctx = document.querySelector("canvas").getContext("2d"), gr = ctx.createLinearGradient(0, 0, 300, 0); // draw something with alpha channel to main canvas gr.addColorStop(0, "rgba(255,140,0,1)"); gr.addColorStop(1, "rgba(255,0,0,0)"); ctx.fillStyle = gr; ctx.fillRect(0, 0, 300, 150); // split the canvas to two new canvas, one for color, one for alpha var maps = splitTexture(ctx); document.body.appendChild(maps.color); // show in DOM for demo document.body.appendChild(maps.alpha); // Split texture: function splitTexture(ctx) { var w = ctx.canvas.width, h = ctx.canvas.height, canvasColor = document.createElement("canvas"), canvasAlpha = document.createElement("canvas"), ctxc = canvasColor.getContext("2d"), ctxa = canvasAlpha.getContext("2d"), idata = ctx.getImageData(0, 0, w, h), data32 = new Uint32Array(idata.data.buffer), // use uint-32 buffer (faster!) len = data32.length, i = 0, p, a, g, adata, adata32, cdata, cdata32; // destinations canvasColor.width = canvasAlpha.width = w; // set same size as source canvasColor.height = canvasAlpha.height = h; cdata = ctxc.createImageData(w, h); // create buffers and uint32 views cdata32 = new Uint32Array(cdata.data.buffer); adata = ctxa.createImageData(w, h); adata32 = new Uint32Array(adata.data.buffer); // splitter loop while(i < len) { p = data32[i]; // source pixel as 32-bit ABGR a = p & 0xff000000; // mask out alpha g = 0xff000000 | (a >>> 8) | (a >>> 16) | (a >>> 24); // grey value adata32[i] = g; // set gray value cdata32[i++] = 0xff000000 | (p & 0xffffff); // set color value } ctxc.putImageData(cdata, 0, 0); // update destinations ctxa.putImageData(adata, 0, 0); return { color: canvasColor, alpha: canvasAlpha } }
body {background:#79c}
<canvas></canvas>
我最終編寫了一個代理上下文,該上下文在兩個畫布之間拆分了繪圖操作。 需要csscolorparser`節點模塊(在NPM上)。
var FakeCtx = function (canvasA, canvasB) {
this.ctxA = canvasA.getContext('2d');
this.ctxB = canvasB.getContext('2d');
this.colorStore = {};
};
var assignContextProperty = function (property, propertyType) {
if (propertyType === 'function') {
FakeCtx.prototype[property] = function () {
var argArray = Array.prototype.slice.call(arguments),
ra = this.ctxA[property].apply(this.ctxA, argArray),
rb = this.ctxB[property].apply(this.ctxB, argArray);
if (ra !== rb) {
var argString = argArray.join(', ');
debug.warn('Console proxy got two different results for calling ' + property + ':');
debug.warn(' Canvas A: ' + property + '(' + argString + ') = ' + ra);
debug.warn(' Canvas B: ' + property + '(' + argString + ') = ' + rb);
}
return ra;
};
} else {
if (property != 'fillStyle' && property != 'strokeStyle') {
FakeCtx.prototype.__defineGetter__(property, function () {
return this.ctxA[property];
});
FakeCtx.prototype.__defineSetter__(property, function (value) {
this.ctxA[property] = this.ctxB[property] = value;
});
} else {
FakeCtx.prototype.__defineGetter__(property, function () {
return this.colorStore[property] || this.ctxA[property];
});
FakeCtx.prototype.__defineSetter__(property, function (value) {
var color = csscolor.parseCSSColor(value);
if (color === null || color.length < 3) {
throw new Error('Invalid color ' + value + ': ' + color);
} else {
this.colorStore[property] = value;
if (color.length === 3 || color[3] === 1) { // no alpha
this.ctxA[property] = value;
this.ctxB[property] = 'rgb(255, 255, 255)'; // white = full alpha
} else {
this.ctxA[property] = 'rgb(' + color.slice(0, 3).join(', ') + ')';
var alphaValue = Math.round(255 * color[3]);
this.ctxB[property] = 'rgb(' + [ alphaValue, alphaValue, alphaValue ].join(', ') + ')';
// console.log('got color with alpha ' + value + ', splitting to ' + 'rgb(' + color.slice(0, 3).join(', ') + ');' + ' and ' + 'rgb(' + [ alphaValue, alphaValue, alphaValue ].join(', ') + ');');
}
}
});
}
}
}
var _ctx = makeCanvas(0, 0).getContext('2d');
for (var property in _ctx) {
assignContextProperty(property, typeof _ctx[property]);
}
該代碼可能有幾個錯誤,我還沒有通過任何嚴格的測試來運行它,但是它只適用於簡單的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.