We are updating our three.js app setup so that it uses the THREE.ACESFilmicToneMapping (because our scene uses IBL from an EXR environment map).
In that process, materials using textures are now looking great (map colours used to be washed out before the change as illustrated below).
with renderer.toneMapping = THREE.LinearToneMapping (default)
with renderer.toneMapping = THREE.ACESFilmicToneMapping
However, the problem is that plain colours (without any maps) are now looking burnt...
with renderer.toneMapping = THREE.LinearToneMapping (default)
with renderer.toneMapping = THREE.ACESFilmicToneMapping
It's now totally impossible to get bright yellow or green for example. Turning down the renderer.toneMappingExposure or the material.envMapIntensity can help, but materials with textures then get way too dark... Ie. provided any given parameter, material using plain colours are either too bright, or material using textures are too dark.
I'm not sure if I am missing something, but this looks like there would be an issue in this setup. Would there be any other parameter that we are overlooking that is causing this result?
Otherwise, we are loading all our models using the GLTFLoader, and we have renderer.outputEncoding = THREE.sRGBEncoding; as per the documentation of the GLTFLoader.
Our environment map is an equirectangular EXR loaded with EXRLoader:
import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader';
const envMapLoader = new EXRLoader();
envMapLoader.load(
environmentMapUrl,
rawTexture => {
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();
const envMapTarget = pmremGenerator.fromEquirectangular(rawTexture);
const { texture } = envMapTarget;
return texture;
},
...
)
The short answer is that this is expected behaviour and there will always be tradeoffs in lightning/colors. One has thus to empirically select settings depending on the specific setup/application and desired results.
From Don McCurdy's comment directly on my question above:
You may need to go to the three.js forums for this question. There is no quick fix to add HDR lighting to colors that are already
100% saturated. Lighting is not a simple topic, and different
tonemapping methods make different tradeoffs here.
Related
I'm trying to create an overlay in ArcGIS that has moving graphics/symbols which are updated by coordinates received from roving devices. I'm able to display a simple symbol initially but cannot get it to move on the map. My test code is
GraphicsOverlay machineOverlay = new GraphicsOverlay();
MainMapView.GraphicsOverlays.Add(machineOverlay);
MapPointBuilder rdLocation = new MapPointBuilder(150.864119200149, -32.3478640837185, SpatialReferences.Wgs84);
SimpleMarkerSymbol sRD1234 = new SimpleMarkerSymbol()
{
Color = System.Drawing.Color.Red,
Size = 10,
Style = SimpleMarkerSymbolStyle.Circle
};
Graphic graphicWithSymbol = new Graphic(rdLocation.ToGeometry(), sRD1234);
machineOverlay.Graphics.Add(graphicWithSymbol);
// here the red circle is displayed correctly on the map
rdLocation.SetXY(150.887115, -32.357600);
rdLocation.ReplaceGeometry(rdLocation.ToGeometry());
// here I expect the red circle to move but it doesn't
Do I need to trigger an event to "re-render" or refresh the overlay, or what do I need to do to get the graphic to move on my map?
There was a similar question here and the answer was "just update the geometry" which is what I'm attempting to do, but with no success.
If there is an entirely different or better approach to moving markers on a map please suggest, I'm just getting started in the ArcGIS runtime.
Thanks
After a lot of searching I replaced one line of code and its now working
//rdLocation.ReplaceGeometry(rdLocation.ToGeometry());
graphicWithSymbol.Geometry = rdLocation.ToGeometry();
It seems I misunderstood the function of ReplaceGeometry(). Any clarification on this would be helpful.
I'm trying to apply a pixelation shader to my textures and I need it to be applied only once, after that I can reuse my shader generated images as textures over and over without having to calculate every single time.
so how do I take a few images -> apply a shader and render them only once every time the game loads -> and use them as my textures?
so far I've managed to find the shader to apply:
shader_type canvas_item;
uniform int amount = 40;
void fragment()
{
vec2 grid_uv = round(UV * float(amount)) / float(amount);
vec4 text = texture(TEXTURE, grid_uv);
COLOR = text;
}
but I have no idea how to render out the images using it
Shaders reside in the GPU, and their output goes to the screen. To save the image, the CPU would have to see the GPU output, and that does not happen… Usually. And since it does not go through the CPU, the performance is good. Usually. Well, at least it is better than if the CPU was doing it all the time.
Also, are you sure you don't want to get a pixel art look by other means? Such as removing filter from the texture, changing the stretch mode and working on a small resolution, and perhaps enable pixel snap? No? Watch How to make a silky smooth camera for pixelart games in Godot. Still No? Ok...
Anyway, for what you want, you are going to need a Viewport.
Viewport setup
What you will need is to create a Viewport. Don't forget to set its size. Also may want to set render_target_v_flip to true, this flips the image vertically. If you find the output image is upside down it is because you need to toggle render_target_v_flip.
Then place as child of the Viewport what you want to render.
Rendering
Next, you can read the texture form the Viewport, convert it to an image, and save it to a png. I'm doing this on a tool script attached to the Viewport, so I'll have a workaround to trigger the code from the inspector panel. My code looks like this:
tool
extends Viewport
export var save:bool setget do_save
func do_save(new_value) -> void:
var image := get_texture().get_data()
var error := image.save_png("res://output.png")
if error != OK:
push_error("failed to save output image.")
You can, of course, export a FILE path String to ease changing it in the inspector panel. Here I'm handing common edge cases:
tool
extends Viewport
export(String, FILE) var path:String
export var save:bool setget do_save
func do_save(_new_value) -> void:
var target_path := path.strip_edges()
var folder := target_path.get_base_dir()
var file_name := target_path.get_file()
var extension := target_path.get_extension()
if file_name == "":
push_error("empty file name.")
return
if not (Directory.new()).dir_exists(folder):
push_error("output folder does not exist.")
return
if extension != "png":
target_path += "png" if target_path.ends_with(".") else ".png"
var image := get_texture().get_data()
var error := image.save_png(target_path)
if error != OK:
push_error("failed to save output image.")
return
print("image saved to ", target_path)
Another option is to use ResourceSaver:
tool
extends Viewport
export var save:bool setget do_save
func do_save(new_value) -> void:
var image := get_texture().get_data()
var error := ResourceSaver.save("res://image.res", image)
if error != OK:
push_error("failed to save output image.")
This will only work from the Godot editor, and will only work for Godot, since you get a Godot resource file. Although I find interesting the idea of using Godot to generate images. I'm going to suggest going with ResourceSaver if you want to automate generating them for Godot.
About saving resources from tool scripts
In the examples above, I'm assuming you are saving to a resource path. This is because the intention is to use the output image as a resource in Godot. Using a resource path has a couple implications:
This might not work on an exported game (since the goals is improve the workflow, this is OK).
Godot would need to re-import the resource, but will not notice it changed.
We can deal with the second point from an EditorPlugin, if that is what you are doing, you can do this to tell Godot to scan for changes:
get_editor_interface().get_resource_filesystem().scan()
And if you are not, you can cheat by creating an empty EditorPlugin. The idea is to do this:
var ep = EditorPlugin.new()
ep.get_editor_interface().get_resource_filesystem().scan()
ep.free()
By the way, you will want to cache cache the EditorPlugin instead of making a new one each time. Or better yet, cache the EditorFileSystem you get from get_resource_filesystem.
Automation
Now, I'm aware that it can be cumbersome to have to place things inside the Viewport. It might be Ok for your workflow if you don't need to do it all the time.
But what about automating it? Well, regardless of the approach, you will need a tool script that makes a hidden Viewport, takes a Node, checks if it has a shader, if it does, it moves it temporarily to the Viewport, get the rendered texture (get_texture()) sets it as the texture of the Node, removes the shader, and returns the Node to its original position in the scene. Or instead of looking for a shader in the Node, always apply a shader to whatever Node, perhaps loaded as a resource instead of hard-coded.
Note: I believe you need to let an idle frame pass between adding the Node to the Viewport and getting the texture, so the texture updates. Or was it two idle frames? Well, if one does not work, try adding another one.
About making an EditorPlugin
As you know, you can create an addon from project settings. This will create an EditorPlugin script for you. There you can either add an option to the tools menu (with add_tool_menu_item), or add it to the tool bar of the editor (with add_control_to_container). And have it act on the current selection in the edited scene (you can either use get_selection, or overwrite the edit and handles methods). You may also want to make an undo entry for that, see get_undo_redo.
Or, alternatively you can have it keep track (or look for) the Nodes it has to act upon, and then work on the build virtual method, which runs when the project is about to run. I haven't worked with the build virtual method, so I don't know if it has any quirks to gotchas to be aware of.
I'm trying to set a different divIcon for each point on a leaflet geoJson layer. I have tried everything under the sun but it just doesn't work for me. This is what I'm doing
geoJsonLayer = L.geoJson(null, {
pointToLayer: function(feature, latlng) {
var smallIcon = L.DivIcon.extend({
options: {
iconSize: [27, 27],
html: "<div>" + feature.properties.FEATURE_STYLE.SVG_ELEMENT + "</div>"
}
});
return L.marker(latlng, {icon: new smallIcon()});
},
style: getLayerStyle,
onEachFeature: setFeatureProperties,
});
geoJsonLayer.addTo(baseMap);
feature.properties.FEATURE_STYLE.SVG_ELEMENT is an html <svg> containing the icon.
The icons are displayed ok, but every feature display the same icon.
I've also tried doing the following:
using L.Icon with different .png in iconUrl for each feature
using L.circleMarker with different colors for each feature
They both works as expected (different color / icon per feature). But I can't seem to get the divIcon to display differently for each feature.
Anyone have idea why this is the case?
Thanks in advance.
UPDATE:
This is what feature.properties.FEATURE_STYLE.SVG_ELEMENT looks like
Your code to instantiate a new L.divIcon is more complicated than really necessary, but it works, not considering the SVG part:
https://jsfiddle.net/3v7hd2vx/236/
That being said, please note that:
style option is used for vector layers. Therefore in the case of Point features that are rendered as L.divIcon's, it is not used.
onEachFeature option is applied after the pointToLayer one, because the latter is needed to create the layer that is fed to onEachFeature. Therefore if you build the feature.properties.FEATURE_STYLE.SVG_ELEMENT in there (as the name of your function setFeatureProperties suggests), it is too late.
If you need further help, you would very probably need to share more code, e.g. the style and onEachFeature options, and some sample data, in particular with feature.properties.FEATURE_STYLE.SVG_ELEMENT.
I need to create speed gauge with SVG. As the speed changes, a needle is rotated to indicate the proper speed on the gauge, and an arc is drawn around the gauge's circumference following the tip of the needle.
I have attempted to use three different libraries (VelocityJS, SnapSVG, and GSAP) to solve issues with the needle's rotation, but I have not succeeded yet in finding an implementation that works.
My initial attempts were with Velocity. I got it working in all browsers except IE. In IE, all attempts to change transform-origin failed.
Then I tried both SnapSVG and GSAP, but two issues keep coming up:
The needle's rotation mostly works well, but occasionally it rotates in the wrong direction, under the gauge, no doubt following the shortest distance to the point.
In IE, stroke-dashoffset causes unpredictable results.
I have created a CodePen that shows the gauge's behaviour when driven by either of these three libraries.
Any help?
Snap version works fine for me, but I'm guessing the problem as mentioned is stroke-dashoffset which I can't test in IE.
One possibility if stroke-dashoffset is not possible, is to rebuild the path string each time. Uses a bit more resources, but I think may be ok.
Otherwise you could try drawing a mask or clip the same size as the arc over it, and animate that, but it will use more resources as well.
Here is a Snap solution, rebuilding the arc path each time.
Amended code...
var arc = Snap.select('#gauge-arc');
var arcLength = arc.getTotalLength();
var arcString = arc.attr('d');
arc.attr({ d: ''})
Snap.animate(0,arcLength, function( val ) {
var arcSubPath = Snap.path.getSubpath(arcString,0,val) ;
arc.attr({ d: arcSubPath });
}, 100, function() {
Snap.animate(arcLength,0, function( val ) {
var arcSubPath = Snap.path.getSubpath(arcString,0,val) ;
arc.attr({ d: arcSubPath });
},500);
})
},
Example fiddle (note, the other buttons probably won't work as I've removed the stroke-dashoffset in the svg markup).
The Spotify UI guidelines for Spotify apps (at https://developer.spotify.com/technologies/apps/guidelines/design/) say "When listing tracks in your app, use our standardized track listings". I cannot find any examples in the documentation on how to use these "standardized track listings". By using the Inspector I have found classes in list.css (such as sp-list and sp-item) which it looks like I need to use but have not been able to work out quite how to use these to recreate the look of the Spotify track listings.
The Billboard Top Charts app appears to use track listings like I need, but I can't find ay way to see how they are doing this as the Inspector only works for your own apps as far as I can tell.
Does anybody have any advice or examples?
Some examples
sp = getSpotifyApi(1);
var m = sp.require("sp://import/scripts/api/models");
var v = sp.require("sp://import/scripts/api/views");
// Example 1
var tpl = new m.Playlist();
var tempList = new v.List(tpl);
tpl.add(m.Track.fromURI("spotify:track:4z4t4zEn4ElVPGmDWCzRQf"));
tpl.add(m.Track.fromURI("http://open.spotify.com/track/7E8JGVhbwWgAQ1DtfatQEl"));
tpl.add(m.Track.fromURI("spotify:track:40YBc3mR3yyqyYvtesQOMj"));
tpl.add(m.Track.fromURI("spotify:local:Rolling+Stones:A+Bigger+Bang:Rain+Fall+Down:293"));
document.body.appendChild(tempList.node);
// Example 2
var pl = m.Playlist.fromURI("spotify:user:username:playlist:424km2k4m24");
var list = new v.List(pl);
document.body.appendChild(list.node);
// Example 3
var album = m.Album.fromURI("spotify:album:1vWnB0hYmluskQuzxwo25a");
var albumList = new v.List(album);
albumList.node.classList.add("album");
document.body.appendChild(albumList.node);
Thanks for asking, I had exactly the same question too!
I too get the issue where i get no actual content added - just the wrapper div. Not including api.css makes it work, but the list is obviously not styled. Including css/list.css directly breaks it too. Creating my own copy and selectively commenting out list.css I found the offending rule to be:
.sp-list > div {
if you change this to be
.sp-list {
then it renders fine. No idea what is going on. Obviously this solution is not idea because I've just duplicated what's meant to be a common resource...