How to render and set custom mipmaps for THREE.WebGLRenderTarget - javascript

I'd like to generate custom mip maps for a rendered frame for an effect in THREE.js, but it's not clear how to achieve that. I'll want to regenerate the mip maps again after every frame.
This example shows how to generate mipmaps for a texture, but it looks like the mipmaps array requires a handle to an image or canvas tag, which a renderTarget texture does not have. Ideally I'd like to be able to do something like this:
var rt = new THREE.WebGLRenderTarget()
// initialize and render to the target
var mipmaps = [];
var width = rt.width / 2;
var height = rt.height / 2;
while( width > 2 && height > 2) {
var mip = new THREE.WebGLRenderTarget({ width, height });
mipmaps.push(mip.texture); // mip.texture.image === undefined
// render to the new mipmap with a custom downsample shader
width /= 2;
height /= 2;
}
rt.texture.mipmaps = mipmaps;
// render using the original render target as a texture map and using
// the new mip maps as needed
Is this possible? How would I achieve this?
Thank you!

Related

Manually change size of Texture in ThreeJS

Default wrapS and wrapT is THREE.ClampToEdgeWrapping, which mean, that if I have square picture and geometry like that const geometry = new BoxGeometry(3, 2, 1); I will lose proportion of the source. THREE.RepeatWrapping and THREE.MirroredRepeatWrapping are not suitable for my case, because I need only one image on each side in the center. Is there any way to set width and height of image that is loaded (and maybe change size on each side of сuboid)? The way I'm trying to do it now (not working):
const geometry = new BoxGeometry(9.89326190948486, 0.953460395336151, 3);
const material = new MeshStandardMaterial({
map: new TextureLoader().load(
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjU2cHgiIGhlaWdodD0iMjU2cHgiIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0IiA+PHJlY3QgaWQ9InN2Z0VkaXRvckJhY2tncm91bmQiIHg9IjAiIHk9IjAiIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2IiBzdHlsZT0iZmlsbDogbm9uZTsgc3Ryb2tlOiBub25lOyIvPjxjaXJjbGUgaWQ9ImUxX2NpcmNsZSIgY3g9IjEyMC4wMCIgY3k9IjExNi4wMCIgc3R5bGU9ImZpbGw6d2hpdGU7c3Ryb2tlOmJsYWNrO3N0cm9rZS13aWR0aDo5cHgiIHI9IjEyNy40NiIgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDcxMDcgMC43MDcxMDcgLTAuNzA3MTA3IDAuNzA3MTA3IDEyOC4xNzIgLTQxLjg3NzIpIi8+PGNpcmNsZSBpZD0iZTJfY2lyY2xlIiBjeD0iMTEzLjAwIiBjeT0iMTEyLjAwIiBzdHlsZT0iZmlsbDpyb3N5YnJvd247c3Ryb2tlOmJsYWNrO3N0cm9rZS13aWR0aDoxcHgiIHI9IjQzLjg2Ii8+PC9zdmc+',
function () {
// texture still lose proportion and behaves as THREE.ClampToEdgeWrapping
material.map.image.height = 3;
material.map.image.width = 3;
material.map.needsUpdate = true;
}),
color: new Color(0.7, 0.7, 0.7)
});
Screenshot of what I got with setting height and width to 128. This thing should be a circle
Screenshot of what I got with setting height and width to 0.9. Also should be a circle.

How to get texture dimensions with Three.js

I would like to know wether it is possible to get the dimensions of a texture ?
I used this line to set my texture :
const texture = THREE.ImageUtils.loadTexture(src)
Maybe it is necessary to load the image to get its dimensions (but is it possible only with javascript, not html and its div ?) ?
I would like the material I create afterwards to fit the texture dimensions.
First, THREE.ImageUtils.loadTexture is deprecated in the latest THREE.js (r90). Take a look at THREE.TextureLoader instead.
That said, you can get to the image and its properties from a loaded texture.
texture.image
Depending on the image format, you should be able to access the width/height properties, which will be your texture's dimensions.
Just a note: Loading a texture is asynchronous, so you'll need to define the onLoad callback.
var loader = new THREE.TextureLoader();
var texture = loader.load( "./img.png", function ( tex ) {
// tex and texture are the same in this example, but that might not always be the case
console.log( tex.image.width, tex.image.height );
console.log( texture.image.width, texture.image.height );
} );
If you turn off sizeAttenuation, and you have a function that scales the Sprite according to the desired width, then this function will be like:
scaleWidth(width) {
const tex = sprite.material.map;
const scaleY = tex.image.height / tex.image.width;
sprite.scale.setX(width).setY(width * scaleY);
}
So, at this point, you can set the scale according to the desired width, maintaining the aspectRatio of the image.
Then you must have a function that receives the camera, and depending on the type of camera, updates the sprite's width:
updateScale(cam) {
let cw = 1;
if(cam.isOrthographicCamera) {
cw = cam.right - cam.left;
}
else if(cam.isPerspectiveCamera) {
cw = 2 * cam.aspect * Math.tan(cam.fov * 0.5 * 0.01745329);
}
scaleWidth(cw * desiredScaleFactor);
}

How to connect objects with Bezier-like curves using Paper.js

I have a web app prototype where nodes similar to Blender shader editor are connected to each other. I am using Paper.js framework
I want them to be connected using those smooth Bezier-like curves. So I have 2 shapes and I can connect them by making a straight line, but now I want to have handles at the endpoints that smooth these objects out, kinda like this:
So 2 handles on endpoints, pointing horizontally for half the bounding box of the path.
The problem is I can't figure out how to add and edit those handles using Paper.js
The code I have is this:
function makeRectangle(topLeft, size, cornerSize, colour){
var rectangle = new Rectangle(topLeft, size);
var cornerSize = cornerSize;
var path = new Path.RoundRectangle(rectangle, cornerSize);
path.fillColor = colour;
return path;
}
var xy1 = new Point(50,50); //Position of 1st rectangle.
var size = new Size(100, 80); //Size
var c = new Size(8,8); //Corner radius
var col = "#167ee5"; //Colour
var r1 = makeRectangle(xy1, size, c, col); //Make first rectangle
var xy2 = new Point(467,310); //Position of second rectangle
var size2 = new Size(115, 70); //Size of second rectangle
var r2 = makeRectangle(xy2, size2, c, col); //Make secont rectangle
var r1cent = r1.bounds.center; //Get the center points, they will be used as endpoints for the curve.
var r2cent = r2.bounds.center;
var connector = new Path(r1cent, r2cent); //Ok so I made this path... Now what? How do access and edit the handlers at endpoints like in the image?
connector.strokeColor = 'black'; //Give it some colour so we can see it.
You can paste all this code here without any setup, it's a good way to test the framework.
You can use Segment objects when defining the connector rather than using Points (or you can set the handleIn and handleOut properties after creating the path).
The doc is here: Segment
And here is a sketch showing how to use handleIn and handleOut with your code:
sketch.paperjs.org solution

Zoom my drawing on the background [duplicate]

This question already has an answer here:
HTML5 canvas zoom where mouse coordinates
(1 answer)
Closed 8 years ago.
I make program like a paint with HTML5 canvas and javascript. Drawing takes place on the background image. How to zoom my drawing on the background together.
Before zoom it:
After zoom it (need this result):
Note: zoom should be where clicked with the mouse on the background image
I've done this before!
First of all, I set a zoom level attribute on my canvas.
Main.canvas.zoomX = 1;
Main.canvas.zoomY = 1;
I also retain the original size of the canvas for reference.
Main.canvas.originW = Main.canvas.width;
Main.canvas.originH = Main.canvas.height;
I also retain the original left and top of the canvas for reference.
Main.canvas.gLeftStart = 0;
Main.canvas.gTopStart = 0;
I then set a zoom percentage. The zoom level will be adjusted by this amount every time that the zoom event occurs.
Main.canvas.zoomPerc = 0.05;
Next, I set an event listener on my canvas to watch for mousewheel.
Main.canvas.addEventListener('wheel', zoom, true);
Now, I'm going to write a quick function to retrieve the zoom, then I'll explain it.
function zoom(evt)
{
var x;
var y;
Main.canvas.xLayerS = (evt.layerX + (Main.canvas.gLeftStart * -1)) / (Main.canvas.originW * Main.canvas.zoomX);
Main.canvas.yLayerS = (evt.layerY + (Main.canvas.gTopStart * -1)) / (Main.canvas.originH * Main.canvas.zoomY);
Main.canvas.leftPerc = Main.canvas.gLeftStart / (Main.canvas.originW * Main.canvas.zoomX);
Main.canvas.topPerc = Main.canvas.gTopStart / (Main.canvas.originH * Main.canvas.zoomY);
if(evt.deltaY > 1)
{
Main.canvas.zoomX *= 1 + Main.canvas.zoomPerc;
Main.canvas.zoomY *= 1 + Main.canvas.zoomPerc;
}
else
{
Main.canvas.zoomX *= 1 - Main.canvas.zoomPerc;
Main.canvas.zoomY *= 1 - Main.canvas.zoomPerc;
}
var iiDS;
var cmd;
Main.canvas.xLayer = Main.canvas.xLayerS * (Main.canvas.originW * Main.canvas.zoomX);
Main.canvas.yLayer = Main.canvas.yLayerS * (Main.canvas.originH * Main.canvas.zoomY);
Main.context.clearRect(0, 0, Main.canvas.width, Main.canvas.height);
Main.context.beginPath();
Main.canvas.gLeftStart = (evt.layerX - Main.canvas.xLayer);
Main.canvas.gTopStart = (evt.layerY - Main.canvas.yLayer);
for(iiDS = 0; iiDS < Main.dataPoints.length; iiDS++)
{
if(iiDS === 0)
{
cmd = 'moveTo';
}
else
{
cmd = 'lineTo';
}
Main.dataPoints[iiDS].xPerc = Main.dataPoints[iiDS].x / Main.range.x;
Main.dataPoints[iiDS].yPerc = Main.dataPoints[iiDS].y / Main.range.y;
x = Main.canvas.gLeftStart + (Main.dataPoints[iiDS].xPerc * (Main.canvas.originW * Main.canvas.zoomX));
y = Main.canvas.gTopStart + (Main.dataPoints[iiDS].yPerc * (Main.canvas.originH * Main.canvas.zoomY));
Main.context[cmd](x, y);
}
Main.context.stroke();
}
Now that your canvas has been re-sized, you will need to redraw whatever was in it. Remember, any time that you re-size a canvas, you clear the canvas. If your canvas was holding an image, then that's simple, redraw that image at that size. If you canvas was holding data points (like a chart) then I would suggest that you make your data points have percentage like (probably a word for that) positions along your chart, not pixel positions.
More importantly though, I do not suggest that you ever re-size and re-position your canvas on zoom. Your page can get jumbled up and sloppy that way. Instead, use the percentages for size (like I showed you) and use the values for left and top positioning as starting points in your drawing. If a data point was a certain percentage of a way across a chart, it can be drawn at any size. Plus, you can draw outside of your canvas, it just won't be visible. Your canvas would then be more like a view-port.
You can do some really impressive charting this way, which a lot of companies pay a lot of money for. Have fun!
Did you try Context2d.scale(x, y)? You could do the following
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.scale(2, 2);
paintBackGround(context);
paintForeGround(context);
scale(factorWidth, factorHeight) Scales all coordinates in the canvas by the factors, so it will scale the background and the drawing. The example would double the size. You don't have to scale your coordinates by yourself, just let canvas do that for you.
Here is an example :
http://www.html5canvastutorials.com/advanced/html5-canvas-transform-scale-tutorial/
The only problem here: you need to scale before you draw, so you need a model that contains the original drawing in original unscaled coordinates, that can be drawn after scaling (paintForeGround() in my example)
Scale() is part of so called Transformations. You can Translate (move along a vector) rotate and scale the content of a canvas by using buildin functions of canvas. Just take a look at the html5canvastutorials. This works with matrix-mutliplications in the background, but it is really simple to use.

Getting window dimensions in a Metro app

How do I get the dimensions of the window in a windows 8 metro app?
I want to fill the screen with a canvas element, and currently my default.js file looks something like this
// ... some autogenerated code ...
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
// ... some autogenerated code ...
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.width = 600; // I want to set width and height here so that
canvas.height = 800; // canvas fills the entire screen/window.
};
// ... more autogenerated code ...
To get the size, you need:
window.outerWidth
window.outerHeight
This will return the logical size with scale factors already applied.
Note that you also want to listen for View State Changes, and when you enter/leave snapped, fill, full modes to make sure that your UI adjusts to the new window sizes.
Specifically, you need to either use CSS media query matching:
var snappedNotification = matchMedia("all and (-ms-view-state: snapped)");
snappedNotification.addEventListener(mySnappedFunction);
Or listen for window.resize, and use the current view state to look at the current view:
var currentViewState = Windows.UI.ViewManagement.ApplicationView.value;
http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.viewmanagement.applicationviewstate.aspx
Physical dimensions can be obtained this way:
var displayInformation = Windows.Graphics.Display.DisplayInformation.getForCurrentView();
var scale = displayInformation.resolutionScale;
var height = Math.ceil(window.outerHeight * scale / 100);
var width = Math.ceil(window.outerWidth * scale / 100);
var physicalWidth = width / displayInformation.rawDpiX; // in inches
var physicalHeight = height / displayInformation.rawDpiY; // in inches
var physicalSize = Math.floor(Math.sqrt(Math.pow(physicalWidth, 2) + Math.pow(physicalHeight, 2)) * 10) / 10; // in inches
I have tried this on several screen sizes and the physicalSize will be accurate in most cases, sometimes with 0.1" error.
I hope it can be helpful.
The following JavaScript code should work:
var height = $('#bodyTag').outerHeight(true);
var width = $('#bodyTag').outerWidth(true);
You can also use the resolutionScale enum if you want to size your canvas based on scale:
var resolutionScale = Windows.Graphics.Display.DisplayProperties.resolutionScale;

Categories

Resources