Related
I created the code sandbox below where I try to show a mesh.
https://codesandbox.io/s/template-shader-opcqst?file=/src/App.js
For the moment, the mesh is shown but not animated. I don't know how to make change the 'time' parameter of this mesh. It must be linked with a good use of useMemo, useEffect or useFrame.
The code of the mesh is taken from this codepen where it is edited with three.js not fiber :
https://codepen.io/marioecg/pen/mdrvgpq (link to three.js package is blocked by cors)
import * as THREE from "https://cdn.skypack.dev/three#0.124.0";
import { OrbitControls } from "https://cdn.skypack.dev/three/examples/jsm/controls/OrbitControls";
import * as dat from "https://cdn.skypack.dev/dat.gui#0.7.7";
const gui = new dat.GUI();
const settings = {
speed: 0.2,
density: 1.5,
strength: 0.2,
frequency: 3.0,
amplitude: 6.0,
intensity: 7.0,
};
const folder1 = gui.addFolder('Noise');
const folder2 = gui.addFolder('Rotation');
const folder3 = gui.addFolder('Color');
folder1.add(settings, 'speed', 0.1, 1, 0.01);
folder1.add(settings, 'density', 0, 10, 0.01);
folder1.add(settings, 'strength', 0, 2, 0.01);
folder2.add(settings, 'frequency', 0, 10, 0.1);
folder2.add(settings, 'amplitude', 0, 10, 0.1);
folder3.add(settings, 'intensity', 0, 10, 0.1);
const noise = `
// GLSL textureless classic 3D noise "cnoise",
// with an RSL-style periodic variant "pnoise".
// Author: Stefan Gustavson (stefan.gustavson#liu.se)
// Version: 2011-10-11
//
// Many thanks to Ian McEwan of Ashima Arts for the
// ideas for permutation and gradient selection.
//
// Copyright (c) 2011 Stefan Gustavson. All rights reserved.
// Distributed under the MIT license. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
vec3 mod289(vec3 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x)
{
return mod289(((x*34.0)+1.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
vec3 fade(vec3 t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
// Classic Perlin noise, periodic variant
float pnoise(vec3 P, vec3 rep)
{
vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period
Pi0 = mod289(Pi0);
Pi1 = mod289(Pi1);
vec3 Pf0 = fract(P); // Fractional part for interpolation
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
vec4 iy = vec4(Pi0.yy, Pi1.yy);
vec4 iz0 = Pi0.zzzz;
vec4 iz1 = Pi1.zzzz;
vec4 ixy = permute(permute(ix) + iy);
vec4 ixy0 = permute(ixy + iz0);
vec4 ixy1 = permute(ixy + iz1);
vec4 gx0 = ixy0 * (1.0 / 7.0);
vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
gx0 = fract(gx0);
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
vec4 sz0 = step(gz0, vec4(0.0));
gx0 -= sz0 * (step(0.0, gx0) - 0.5);
gy0 -= sz0 * (step(0.0, gy0) - 0.5);
vec4 gx1 = ixy1 * (1.0 / 7.0);
vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
gx1 = fract(gx1);
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
vec4 sz1 = step(gz1, vec4(0.0));
gx1 -= sz1 * (step(0.0, gx1) - 0.5);
gy1 -= sz1 * (step(0.0, gy1) - 0.5);
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
g000 *= norm0.x;
g010 *= norm0.y;
g100 *= norm0.z;
g110 *= norm0.w;
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
g001 *= norm1.x;
g011 *= norm1.y;
g101 *= norm1.z;
g111 *= norm1.w;
float n000 = dot(g000, Pf0);
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
float n111 = dot(g111, Pf1);
vec3 fade_xyz = fade(Pf0);
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
return 2.2 * n_xyz;
}
`;
const rotation = `
mat3 rotation3dY(float angle) {
float s = sin(angle);
float c = cos(angle);
return mat3(
c, 0.0, -s,
0.0, 1.0, 0.0,
s, 0.0, c
);
}
vec3 rotateY(vec3 v, float angle) {
return rotation3dY(angle) * v;
}
`;
const vertexShader = `
varying vec2 vUv;
varying float vDistort;
uniform float uTime;
uniform float uSpeed;
uniform float uNoiseDensity;
uniform float uNoiseStrength;
uniform float uFrequency;
uniform float uAmplitude;
${noise}
${rotation}
void main() {
vUv = uv;
float t = uTime * uSpeed;
float distortion = pnoise((normal + t) * uNoiseDensity, vec3(10.0)) * uNoiseStrength;
vec3 pos = position + (normal * distortion);
float angle = sin(uv.y * uFrequency + t) * uAmplitude;
pos = rotateY(pos, angle);
vDistort = distortion;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.);
}
`;
const fragmentShader = `
varying vec2 vUv;
varying float vDistort;
uniform float uTime;
uniform float uIntensity;
vec3 cosPalette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}
void main() {
float distort = vDistort * uIntensity;
vec3 brightness = vec3(0.5, 0.5, 0.5);
vec3 contrast = vec3(0.5, 0.5, 0.5);
vec3 oscilation = vec3(1.0, 1.0, 1.0);
vec3 phase = vec3(0.0, 0.1, 0.2);
vec3 color = cosPalette(distort, brightness, contrast, oscilation, phase);
gl_FragColor = vec4(color, 1.0);
}
`;
class Scene {
constructor() {
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setClearColor('black', 1);
this.camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 0, 4);
this.scene = new THREE.Scene();
this.clock = new THREE.Clock();
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.init();
this.animate();
}
init() {
this.addCanvas();
this.addElements();
this.addEvents();
}
addCanvas() {
const canvas = this.renderer.domElement;
canvas.classList.add('webgl');
document.body.appendChild(canvas);
}
addElements() {
const geometry = new THREE.IcosahedronBufferGeometry(1, 64);
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
uTime: { value: 0 },
uSpeed: { value: settings.speed },
uNoiseDensity: { value: settings.density },
uNoiseStrength: { value: settings.strength },
uFrequency: { value: settings.frequency },
uAmplitude: { value: settings.amplitude },
uIntensity: { value: settings.intensity },
},
// wireframe: true,
});
this.mesh = new THREE.Mesh(geometry, material);
this.scene.add(this.mesh);
}
addEvents() {
window.addEventListener('resize', this.resize.bind(this));
}
resize() {
let width = window.innerWidth;
let height = window.innerHeight;
this.camera.aspect = width / height;
this.renderer.setSize(width, height);
this.camera.updateProjectionMatrix();
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.render();
}
render() {
this.controls.update();
// Update uniforms
this.mesh.material.uniforms.uTime.value = this.clock.getElapsedTime();
this.mesh.material.uniforms.uSpeed.value = settings.speed;
this.mesh.material.uniforms.uNoiseDensity.value = settings.density;
this.mesh.material.uniforms.uNoiseStrength.value = settings.strength;
this.mesh.material.uniforms.uFrequency.value = settings.frequency;
this.mesh.material.uniforms.uAmplitude.value = settings.amplitude;
this.mesh.material.uniforms.uIntensity.value = settings.intensity;
this.renderer.render(this.scene, this.camera);
}
}
new Scene();
Here's the bubble that should be created by the code : https://tympanus.net/Tutorials/WebGLBlobs/index3.html
how would you do it ? Thanks.
If you bring the mesh into its own function component, you can then use the useFrame hook to update the uniform every frame using a ref of the mesh. (In this case, you could also apply the ref directly to the material)
function MovingBlob() {
const mesh = useRef()
useFrame(({ clock }) => {
if (mesh.current) {
mesh.current.material.uniforms.uTime.value = clock.elapsedTime;
}
})
return (
<mesh ref={mesh}>
<icosahedronBufferGeometry attach="geometry" args={[1, 64]} />
<shaderMaterial attach="material" {...data} />
</mesh>
)
}
This will update the uniform every frame without triggering any re-rendering in the scene, which would happen if you were calling setState every time.
I am making a solar system simulator in Processing. I have initialsed an array in the Planet class which holdes the textures for the PShape planetary objects.
I have added this array to the constructor. Then I tried to call it in the main class where I have initialised the planets, but I get the following erros:
Main class: Syntax Error - Error on parametre or method declaration near 'void'?
Clearly I am missing something or done something wrong. Thank you for the reply.
// Solar_system_enginee main class
//Solar system and it's gravitation's simulation.
// The central sun applies gravitation to other celestial bodies
PImage background;
PImage sun_Texture;
float cameraX, cameraY;
// zoom and pan
float scale = 0.06;
float xPan = 600;
float yPan = 398;
boolean zoomIn = false;
boolean zoomOut = false;
//includes
Sun sun;
Planet mercury;
Planet venus;
Planet earth;
Planet mars;
Planet jupiter;
Planet saturn;
Planet uranus;
Planet neptune;
Planet pluto;
Planet _planetTextures
//screen setup
void setup() {
//Screen size
size(1200, 799, P3D);
sun_Texture = loadImage("sun.jpg");
// Celestial object properties
// 1; Planet's size
// 2; Planet's color / texture
// 3; Movement speed
// 4; Planet's path color
// 5; Planet's path size
// 6; Distance from Sun
// 7; Planet's number of moons // need to check it
//Spawn celestial objects
sun = new Sun(696, color(255, 50, 0));
mercury = new Planet(5, 0.2, 2, color(255, 200, 100), 1448, _planetTextures[0]);
venus = new Planet(12, 0.2, 2, color(150, 10, 100), 1499, _planetTextures[1]);
earth = new Planet(13, 0.2, 2, color(255, 255, 100, 100), 1540, _planetTextures[2]);
//mars = new Planet(7, 0.5, 2, color(167, 45, 160), 1618);
//jupiter = new Planet(143, 0.5, 2, color(167, 45, 160), 2169);
//saturn = new Planet(121, 0.5, 2, color(167, 45, 160), 2825);
//uranus = new Planet(51, 0.5, 2, color(167, 45, 160), 4263);
//neptune = new Planet(50,0.5, 2, color(167, 45, 160), 5886);
//pluto = new Planet(2, 0.5, 2, color(167, 45, 160), 6486);
}
// sceene setup
void sceene() {
// Load background image
background = loadImage("stars.jpg");
background(background);
// Set it in the middle of the screen
translate(width/2, height/2, 0);
}
// draw setup
void draw() {
// Zoom towards the center of the screen, rather the edge of the screen
translate(width/2, height/2, 0);
scale(scale);
// Translate Y and X pan
translate( -xPan, -yPan);
// Draw background
sceene();
// Zoom In
if (zoomIn) {
scale *= 1.01;
}
// Zoom Out
if (zoomOut) {
scale /= 1.01;
}
// Display sun and rotate Y axis camera around sun
sun.display();
sun.move(mouseY);
//mercury
mercury.display();
mercury.move();
//venus
venus.display();
venus.move();
//earth
earth.display();
earth.move();
////mars
//mars.display();
//mars.move();
////jupiter
//jupiter.display();
//jupiter.move();
////saturn
//saturn.display();
//saturn.move();
////uranus
//uranus.display();
//uranus.move();
////neptune
//neptune.display();
//neptune.move();
////pluto
//pluto.display();
//pluto.move();
}
// Key press record function, responsible for zoom in and out of scene
void keyPressed() {
if (keyCode == UP) {
zoomIn = true;
zoomOut = false;
}
if (keyCode == DOWN) {
zoomIn = false;
zoomOut = true;
}
}
void keyReleased() {
if (keyCode == UP) {
zoomIn = false;
}
if (keyCode == DOWN) {
zoomOut = false;
}
}
// Planets class
class Planet {
float radius = 0;
float moveSpeed;
float distance;
float size;
color planetColor;
int pathSize;
color pathColor;
PShape planet;
PImage[] planetTextures = new PImage [3];
Moon moon;
Planet(int _size, float _speed, int _path_size, color _path_color, float _dist, PImage[] _planetTextures) { //color _color
size = _size;
//planetColor = _color;
moveSpeed = _speed;
pathColor = _path_color;
pathSize = _path_size;
distance = _dist;
planetTextures = _planetTextures;
moon = new Moon(10, color(200, 200, 100));
// Set planet's texture
noStroke();
noFill();
planet = createShape(SPHERE, size);
planet.setTexture(_planetTextures[0]);
// Load textures into an array
planetTextures[0] = loadImage ("mercury.jpg");
planetTextures[1] = loadImage ("venus.jpg");
planetTextures[2] = loadImage ("earth.jpg");
}
void display() {
pushMatrix();
//path
stroke(pathColor);
strokeWeight(2);
noFill();
rotateY(radius * moveSpeed);
rotateX(PI/2);
ellipse(0, 0, 2*distance, 2*distance);
//body
noStroke();
fill(planetColor);
translate(distance, 0, 0);
shape(planet);
//sphere(size);
moon.display();
moon.move();
popMatrix();
}
void move() {
radius += moveSpeed;
}
}
The Syntax Error - Missing name or ; near ‘void’? error occurs between Planet _planetTextures and void setup().
Sometimes error can be a bit cryptic: in this case it's as straightforward as it can be: you forgot to add ; at the end of Planet _planetTextures.
That being said, I've spotted a bunch of other areas in your code you might want to revise:
_planetTextures is later used as if it's a PImage[] in setup() when you instantiate planets
the Planet constructor takes in a PImage[] _planetTextures argument. If a single planet uses a single texture, maybe you meant PImage _planetTexture instead ?
The Planet constructor loads 3 images (mercury, venus, earth): should it be task of the main sketch to load all assets, then pass each single PImage to it's respective planet (instead of reloading the same 3 images for every single planet instance)
since the planet texture is only once in the constructor, there's no need for the PImage[] property
the backround PImage was loaded continously, multiple times per frame in sceene() (called from draw()): you might want to load the image once in setup.
Here is your code with the above tweaks applied:
//Solar system and it's gravitation's simulation.
// The central sun applies gravitation to other celestial bodies
PImage background;
PImage sun_Texture;
float cameraX, cameraY;
// zoom and pan
float scale = 0.06;
float xPan = 600;
float yPan = 398;
boolean zoomIn = false;
boolean zoomOut = false;
//includes
Sun sun;
Planet mercury;
Planet venus;
Planet earth;
Planet mars;
Planet jupiter;
Planet saturn;
Planet uranus;
Planet neptune;
Planet pluto;
PImage[] planetTextures;
//screen setup
void setup() {
//Screen size
size(1200, 799, P3D);
sun_Texture = loadImage("sun.jpg");
// Celestial object properties
// 1; Planet's size
// 2; Planet's color / texture
// 3; Movement speed
// 4; Planet's path color
// 5; Planet's path size
// 6; Distance from Sun
// 7; Planet's number of moons // need to check it
// Load background image
background = loadImage("stars.jpg");
// Load textures into an array
planetTextures[0] = loadImage ("mercury.jpg");
planetTextures[1] = loadImage ("venus.jpg");
planetTextures[2] = loadImage ("earth.jpg");
//Spawn celestial objects
sun = new Sun(696, color(255, 50, 0));
mercury = new Planet(5, 0.2, 2, color(255, 200, 100), 1448, planetTextures[0]);
venus = new Planet(12, 0.2, 2, color(150, 10, 100), 1499, planetTextures[1]);
earth = new Planet(13, 0.2, 2, color(255, 255, 100, 100), 1540, planetTextures[2]);
//mars = new Planet(7, 0.5, 2, color(167, 45, 160), 1618);
//jupiter = new Planet(143, 0.5, 2, color(167, 45, 160), 2169);
//saturn = new Planet(121, 0.5, 2, color(167, 45, 160), 2825);
//uranus = new Planet(51, 0.5, 2, color(167, 45, 160), 4263);
//neptune = new Planet(50,0.5, 2, color(167, 45, 160), 5886);
//pluto = new Planet(2, 0.5, 2, color(167, 45, 160), 6486);
}
// sceene setup
void scene() {
background(background);
// Set it in the middle of the screen
translate(width/2, height/2, 0);
}
// draw setup
void draw() {
// Zoom towards the center of the screen, rather the edge of the screen
translate(width/2, height/2, 0);
scale(scale);
// Translate Y and X pan
translate( -xPan, -yPan);
// Draw background
scene();
// Zoom In
if (zoomIn) {
scale *= 1.01;
}
// Zoom Out
if (zoomOut) {
scale /= 1.01;
}
// Display sun and rotate Y axis camera around sun
sun.display();
sun.move(mouseY);
//mercury
mercury.display();
mercury.move();
//venus
venus.display();
venus.move();
//earth
earth.display();
earth.move();
////mars
//mars.display();
//mars.move();
////jupiter
//jupiter.display();
//jupiter.move();
////saturn
//saturn.display();
//saturn.move();
////uranus
//uranus.display();
//uranus.move();
////neptune
//neptune.display();
//neptune.move();
////pluto
//pluto.display();
//pluto.move();
}
// Key press record function, responsible for zoom in and out of scene
void keyPressed() {
if (keyCode == UP) {
zoomIn = true;
zoomOut = false;
}
if (keyCode == DOWN) {
zoomIn = false;
zoomOut = true;
}
}
void keyReleased() {
if (keyCode == UP) {
zoomIn = false;
}
if (keyCode == DOWN) {
zoomOut = false;
}
}
class Planet {
float radius = 0;
float moveSpeed;
float distance;
float size;
color planetColor;
int pathSize;
color pathColor;
PShape planet;
Moon moon;
Planet(int _size, float _speed, int _path_size, color _path_color, float _dist, PImage _planetTexture) { //color _color
size = _size;
//planetColor = _color;
moveSpeed = _speed;
pathColor = _path_color;
pathSize = _path_size;
distance = _dist;
moon = new Moon(10, color(200, 200, 100));
// Set planet's texture
noStroke();
noFill();
planet = createShape(SPHERE, size);
planet.setTexture(_planetTexture);
}
void display() {
pushMatrix();
//path
stroke(pathColor);
strokeWeight(2);
noFill();
rotateY(radius * moveSpeed);
rotateX(PI/2);
ellipse(0, 0, 2*distance, 2*distance);
//body
noStroke();
fill(planetColor);
translate(distance, 0, 0);
shape(planet);
//sphere(size);
moon.display();
moon.move();
popMatrix();
}
void move() {
radius += moveSpeed;
}
}
It would've made it easier to for others to test if you would have also posted the Sun/Moon classes and the textures uses: something to remember for future posts. (The easier it is for others to replicate your issue the more like to get (good) answers).
Not bad progress and you're getting the main points about classes.
There might still be confusion over how arguments get passed from the sketch(global) scope to instances, but that may also be an artefact of perhaps trying to write a lot of code in one go ?
If that's the case, I recommend slowing down, writing one bit of functionality at a time (e.g. one function or class method at a time, running the sketch and testing to ensure it works as expected first). Once a bit of code works as expected to you can move to the next and ideally text combinations of the newly added functionalities to ensure that there no weird interactions between classes. Constantly testing how the code behaves may appear slower, but resting assured the code works as go along is faster on the long run and it definitely makes debugging much easier (likely to be most recently added functionality which is also freshes in your memory). Have fun coding !
I'm learning how to create effects for wpf using hlsl.
I'm currently trying to make a simple effect that marks edges in an image.
I want too use the Sobel operator for this, so I set a public float2x3 in my hlsl code, but I can't seem to access elements in that matrix.
I've tried manually inputting the proper values and it works, but not when I use a loop.
sampler2D imageSampler : register(s0);
float imageWidth : register(c0);
float imageHeight : register(c1);
float threshold : register(c2);
float2x3 op =
{
1.0f, 2.0f, 1.0f,
-1.0f, -2.0f, -1.0f
};
float grayScale(float3 color)
{
return (color.r + color.g + color.b) / 3;
}
float4 GetEdgeLoop(float2 coord, float2 pixelSize)
{
float2 current;
float avrg = 0;
float holder;
float gsHolder;
current.x = coord.x - pixelSize.x;
for (int x = 0; x < 2; x++)
{
current.y = coord.y - pixelSize.y;
for (int y = 0; y < 3; y++)
{
holder = op[x][y];
gsHolder = grayScale(tex2D(imageSampler, current).rgb);
avrg += gsHolder * holder;
current.y += pixelSize.y;
}
current.x += pixelSize.x * 2;
}
avrg = abs(avrg / 8);
if (avrg > threshold)
return float4(1, 0, 0, 1);
return tex2D(imageSampler, coord);
}
float4 main(float2 uv : TEXCOORD) : COLOR
{
float2 pixelSize = (1 / imageWidth, 1 / imageHeight);
return GetEdgeLoop(uv, pixelSize);
}
This method should return the color red for strong enough edges.
This does return red sometimes, but clearly not for edges.
I have another method for detecting edges that actually works, but it samples the required pixels manually:
float4 GetEdge(float2 coord, float2 pixelSize)
{
float avrg = 0;
avrg += grayScale(tex2D(imageSampler, float2(coord.x - pixelSize.x, coord.y - pixelSize.y)).rgb) * 1;
avrg += grayScale(tex2D(imageSampler, float2(coord.x - pixelSize.x, coord.y)).rgb) * 2;
avrg += grayScale(tex2D(imageSampler, float2(coord.x - pixelSize.x, coord.y + pixelSize.y)).rgb) * 1;
avrg += grayScale(tex2D(imageSampler, float2(coord.x + pixelSize.x, coord.y - pixelSize.y)).rgb) * (-1);
avrg += grayScale(tex2D(imageSampler, float2(coord.x + pixelSize.x, coord.y)).rgb) * (-2);
avrg += grayScale(tex2D(imageSampler, float2(coord.x + pixelSize.x, coord.y + pixelSize.y)).rgb) * (-1);
avrg = abs(avrg / 8);
if (avrg > threshold)
return float4(1, 0, 0, 1);
return tex2D(imageSampler, coord);
}
This method isn't very elegant and I want to replace it.
I'm not an expert on shaders in WPF, but in the code that you provided you don't specify any registers for your input, so it's not accessible from the pixel shader code. I would expect something like this to be part of your code:
sampler2D implicitInputSampler : register(S0);
float opacity : register(C0);
(The first definition is a texture sampler, the second one a floating number register.)
You need to 'declare' the objects that will be passed into the shader function. Microsoft's guide to shader effect writing has a nice example at https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.effects.shadereffect?view=netframework-4.8. Here's the code for posterity:
// Threshold shader
// Object Declarations
sampler2D implicitInput : register(s0);
float threshold : register(c0);
float4 blankColor : register(c1);
//------------------------------------------------------------------------------------
// Pixel Shader
//------------------------------------------------------------------------------------
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float intensity = (color.r + color.g + color.b) / 3;
float4 result;
if (intensity > threshold)
{
result = color;
}
else
{
result = blankColor;
}
return result;
}
You can see that the code declares a texture sampler, a float and a float4 (vector with 4 floats) as input. You can then bind to these through DependencyProperties like this:
#region Input dependency property
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(ThresholdEffect), 0);
#endregion
///////////////////////////////////////////////////////////////////////
#region Threshold dependency property
public double Threshold
{
get { return (double)GetValue(ThresholdProperty); }
set { SetValue(ThresholdProperty, value); }
}
public static readonly DependencyProperty ThresholdProperty =
DependencyProperty.Register("Threshold", typeof(double), typeof(ThresholdEffect),
new UIPropertyMetadata(0.5, PixelShaderConstantCallback(0)));
#endregion
///////////////////////////////////////////////////////////////////////
#region BlankColor dependency property
public Color BlankColor
{
get { return (Color)GetValue(BlankColorProperty); }
set { SetValue(BlankColorProperty, value); }
}
public static readonly DependencyProperty BlankColorProperty =
DependencyProperty.Register("BlankColor", typeof(Color), typeof(ThresholdEffect),
new UIPropertyMetadata(Colors.Transparent, PixelShaderConstantCallback(1)));
#endregion
Do note that the PixelShaderConstantCallback has an int parameter which let's you specify the index of the register to which you want to bind the value.
Edit: Ok, so I've read up some more on this topic, played around with your code and got it to work. Here are the changes I've done in no particular order:
I've read that you should declare your array as static const. It does not change during the execution of the shader and it apparently 'might' allow some minor compiler optimizations.
I've changed the float2x3 to float3x3 to fit a Laplace kernel - as we want the edges working from all sides of the image.
I've changed the grayscale function to use weights for the luminosity approach.
Here's the relevant code:
static const float3x3 laplace =
{
-1.0f, -1.0f, -1.0f,
-1.0f, 8.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
};
float grayScaleByLumino(float3 color)
{
return (0.299 * color.r + 0.587 * color.g + 0.114 * color.b);
}
float4 GetEdgeGeorge(float2 coord, float2 pixelSize)
{
float2 current = coord;
float avrg = 0;
float kernelValue;
float4 currentColor;
float grayScale;
float4 result;
current.x = coord.x - pixelSize.x;
for (int x = 0; x < 3; x++)
{
current.y = coord.y - pixelSize.y;
for (int y = 0; y < 3; y++)
{
kernelValue = laplace[x][y];
grayScale = grayScaleByLumino(tex2D(imageSampler, current).rgb);
avrg += grayScale * kernelValue;
current.y += pixelSize.y;
}
current.x += pixelSize.x;
}
avrg = abs(avrg / 8);
if (avrg > threshold)
{
result = float4(1, 0, 0, 1);
}
else
{
result = tex2D(imageSampler, coord);
}
return result;
}
The whole solution I've created is available here: https://github.com/georgethejournalist/WPFShaders. I hope this helps.
Within a MacOS app, I have a pattern defined by a SCNProgram that is mapped to an SCNPlane.
It looks like this:
The shader is supposed to make the rows of triangles shift , like in this video. The video is a screen grab of the same shader running inside of an MTKview.
animated texture
In the SceneKit version of this shader, the shader only animates when I click on the plane within the view.
How do I make the SceneKit view (or Scene?) continually animate the shader all the time? Again this app is on MacOS. I have tried setting
self.gameView!.isPlaying = true
but that doesn't seem to fix the problem
Here is the Metal shader:
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct myPlaneNodeBuffer {
float4x4 modelTransform;
float4x4 modelViewTransform;
float4x4 normalTransform;
float4x4 modelViewProjectionTransform;
float2x3 boundingBox;
};
typedef struct {
float3 position [[ attribute(SCNVertexSemanticPosition) ]];
float2 texCoords [[ attribute(SCNVertexSemanticTexcoord0) ]];
} VertexInput;
static float rand(float2 uv)
{
return fract(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
}
static float2 uv2tri(float2 uv)
{
float sx = uv.x - uv.y / 2;
float sxf = fract(sx);
float offs = step(fract(1 - uv.y), sxf);
return float2(floor(sx) * 2 + sxf + offs, uv.y);
}
struct SimpleVertexWithUV
{
float4 position [[position]];
float2 uv;
};
vertex SimpleVertexWithUV trianglequiltVertex(VertexInput in [[ stage_in ]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant myPlaneNodeBuffer& scn_node [[buffer(1)]])
{
SimpleVertexWithUV vert;
vert.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
vert.uv = in.texCoords;
return vert;
}
fragment float4 trianglequiltFragment(SimpleVertexWithUV in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant myPlaneNodeBuffer& scn_node [[buffer(1)]])
{
float4 fragColor;
float2 uv = in.uv*10;
float timer = scn_frame.time;
uv.y += timer;
float t = timer * 0.8;
float tc = floor(t);
float tp = smoothstep(0, 0.8, fract(t));
float2 r1 = float2(floor(uv.y), tc);
float2 r2 = float2(floor(uv.y), tc + 1);
float offs = mix(rand(r1), rand(r2), tp);
uv.x += offs * 8;
float2 p = uv2tri(uv);
float ph = rand(floor(p)) * 6.3 + p.y * 0.2;
float c = abs(sin(ph + timer));
fragColor = float4(c, c, c, 1);
return(fragColor);
}
here is the view controller:
import SceneKit
import QuartzCore
class GameViewController: NSViewController {
#IBOutlet weak var gameView: GameView!
override func awakeFromNib(){
super.awakeFromNib()
// create a new scene
let scene = SCNScene()
Swift.print(gameView.colorPixelFormat.rawValue)
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
// place the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
// turn off default lighting
self.gameView!.autoenablesDefaultLighting = false
// set the scene to the view
self.gameView!.scene = scene
// allows the user to manipulate the camera
self.gameView!.allowsCameraControl = true
// show statistics such as fps and timing information
self.gameView!.showsStatistics = true
// configure the view
self.gameView!.backgroundColor = NSColor.black
// play it always?
self.gameView!.isPlaying = true
var geometry:SCNGeometry
geometry = SCNPlane(width:10, height:10)
let geometryNode = SCNNode(geometry: geometry)
let program = SCNProgram()
program.fragmentFunctionName = "trianglequiltFragment"
program.vertexFunctionName = "trianglequiltVertex"
let gradientMaterial = SCNMaterial()
gradientMaterial.program = program
gradientMaterial.specular.contents = NSColor.black
gradientMaterial.locksAmbientWithDiffuse = true
geometry.materials = [gradientMaterial]
geometryNode.geometry?.firstMaterial?.lightingModel = .constant
scene.rootNode.addChildNode(geometryNode)
}
}
Try setting:
gameView?.rendersContinuously = true
(You don’t need all those extra ‘self.’s either.)
I created a 5 face box and put a ball inside of it and allowed it to bounce around inside the box. I created this using glOrtho.
I want to create a 3d Brick breaker style game for my course so I want to get my camera down into the box. I changed from glOrtho to gluPerspective. I had to change some values for my box to render correctly but the ball seems to have gone missing unless i put it on the origin.
This is my value initialization:
void variableInits(){
//Floor Plane
pl1.pos.x = 0; pl1.pos.y = -50; pl1.pos.z = 0;
pl1.norm.x = 0; pl1.norm.y = 1; pl1.norm.z = 0;
//Ceiling Plane
pl2.pos.x = 0; pl2.pos.y = 50; pl2.pos.z = 0;
pl2.norm.x = 0; pl2.norm.y = -1; pl2.norm.z = 0;
//Right Wall Plane
pl3.pos.x = 50; pl3.pos.y = 0; pl3.pos.z = 0;
pl3.norm.x = -1; pl3.norm.y = 0; pl3.norm.z = 0;
//Left Wall Plane
pl4.pos.x = -50; pl4.pos.y = 0; pl4.pos.z = 0;
pl4.norm.x = 1; pl4.norm.y = 0; pl4.norm.z = 0;
//Back Wall Plane
pl5.pos.x = 0; pl5.pos.y = 0; pl5.pos.z = -100;
pl5.norm.x = 0; pl5.norm.y = 0; pl5.norm.z = 1;
//Paddle Plane
paddlePlane.max.x=.25; paddlePlane.max.y=.25; paddlePlane.max.z=1;
paddlePlane.min.x=-.25; paddlePlane.min.y=-.25; paddlePlane.min.z=1;
paddlePlane.normals.x=0; paddlePlane.normals.y=0;paddlePlane.normals.z=-0;
//Ball Init
b1.radius = 10;
b1.pathDirection.x = 0; b1.pathDirection.y = 0; b1.pathDirection.z = 0;
b1.pos.x = 0; b1.pos.y = 0, b1.pos.z = -25;
}
So my ball should draw with a radius of 10 on -25 value of the Z axis. Sadly it does not.
Maybe its an issue with my gluPerspective call?
void reshape(int width, int height)
{
if (height==0)
{
height=1;
}
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.0f,50.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Ill go ahead and post my complete code but it is a bit long.
#include <stdio.h>
#include <gl/glut.h>
#include "pHeader.h"
#define EPSILON 1.0e-8
#define ZERO EPSILON
int g_mainWindow = -1;
float g_lightPos[] = {0, 0, 0, -50};
int angle = 0;
int g_moving;
int g_mouse_x;
int g_mouse_y;
int g_x_angle;
int g_y_angle;
double mX,mY,mZ;
float speed = 1;
plane pl1, pl2, pl3, pl4 ,pl5;
paddlePl paddlePlane;
float collDist = 1000;
ball b1;
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state==GLUT_DOWN)
{
g_moving = 1;
g_mouse_x = x;
g_mouse_y = y;
}
else {
g_moving = 0;
}
}
void mouseDrag(int x, int y)
{
int dx, dy;
if (g_moving){
dx = x - g_mouse_x;
dy = y - g_mouse_y;
g_x_angle += dy;
g_y_angle += dx;
g_mouse_x = x;
g_mouse_y = y;
}
}
void paddle(int x, int y){
GLint viewport[4];
GLdouble modelview[16],projection[16];
glGetIntegerv(GL_VIEWPORT,viewport);
glGetDoublev(GL_MODELVIEW_MATRIX,modelview);
glGetDoublev(GL_PROJECTION_MATRIX,projection);
gluUnProject(x,viewport[3]-y,0,modelview,projection,viewport,&mX,&mY,&mZ);
}
/*************************************************
Vector Math
**************************************************/
float dot(vector XYZ , vector nXYZ){
return (XYZ.x * nXYZ.x) + (XYZ.y * nXYZ.x) + (XYZ.z * nXYZ.z);
}
vector cross(vector v1 , vector v2){
vector result;
result.x = v1.y * v2.z - v1.z * v2.y;
result.y = v1.z * v2.x - v1.x * v2.z;
result.z = v1.x * v2.y - v1.y * v2.x;
return result;
}
vector vectScale(float scale, vector v1){
vector result;
result.x = v1.x * scale;
result.y = v1.y * scale;
result.z = v1.z * scale;
return result;
}
vector vectorSub(vector v1, vector v2){
vector result;
result.x = v1.x - v2.x;
result.y = v1.y - v2.y;
result.z = v1.z - v2.y;
return result;
}
vector flipVect(vector v1){
vector result;
result.x = -v1.x;
result.y = -v1.y;
result.z = -v1.z;
return result;
}
vector vectorAdd(vector v1, vector v2){
vector result;
result.x = v1.x + v2.x;
result.y = v1.y + v2.y;
result.z = v1.z + v2.z;
return result;
}
/****************************************************
End Vecotor Math
****************************************************/
void planeCollision(){
//Check Ceiling
if(b1.pos.y + b1.radius >= 50){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl2.norm) , pl2.norm) , b1.pathDirection);
}
//Check Floor
if(b1.pos.y-b1.radius <= -50){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl1.norm) , pl1.norm) , b1.pathDirection);
}
//Check Right Wall
if(b1.pos.x + b1.radius >= 1){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl3.norm) , pl3.norm) , b1.pathDirection);
}
//Check Left Wall
if(b1.pos.x - b1.radius <= -1){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl4.norm) , pl4.norm) , b1.pathDirection);
}
//Check Back Wall
if(b1.pos.z - b1.radius <= -1){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), pl5.norm) , pl5.norm) , b1.pathDirection);
}
//Check paddle
if(b1.pos.z + b1.radius >= paddlePlane.max.z && b1.pos.x >= paddlePlane.min.x && b1.pos.x <= paddlePlane.max.x && b1.pos.y >= paddlePlane.min.y && b1.pos.y <= paddlePlane.max.y){
b1.pathDirection = vectorAdd((2 * dot(flipVect(b1.pathDirection), paddlePlane.normals) , paddlePlane.normals) , b1.pathDirection);
}
}
void drawPlanes(){
glBegin(GL_QUADS);
//Floor
glColor3f(1,0,0);
glNormal3f( 0.0f , 1.0f, 0.0f);
glVertex3f( 050.0f , -050.0f , -100.0f);
glVertex3f(-050.0f , -050.0f , -100.0f);
glVertex3f(-050.0f , -050.0f , 000.0f);
glVertex3f( 050.0f , -050.0f , 000.0f);
//Ceiling
glColor3f(1,0,1);
glNormal3f(0.0f,-1.0f,0.0f);
glVertex3f( 050.0f, 050.0f, -100.0f);
glVertex3f(-050.0f, 050.0f, -100.0f);
glVertex3f(-050.0f, 050.0f, 000.0f);
glVertex3f( 050.0f, 050.0f, 000.0f);
//Right Wall
glColor3f(0,1,0);
glNormal3f( -1.0f , 0.0f, 0.0f);
glVertex3f(050.0f , 050.0f , 000.0f);
glVertex3f(050.0f , 050.0f , -100.0f);
glVertex3f(050.0f ,-050.0f , -100.0f);
glVertex3f(050.0f ,-050.0f, 000.0f);
//LeftWall
glColor3f(0,1,1);
glNormal3f( 1.0f , 0.0f, 0.0f);
glVertex3f(-050.0f , 050.0f , -100.0f);
glVertex3f(-050.0f , 050.0f , 000.0f);
glVertex3f(-050.0f , -050.0f , 000.0f);
glVertex3f(-050.0f , -050.0f , -100.0f);
//Back Wall
glColor3f(0,0,1);
glNormal3f( 0.0f , 0.0f, 1.0f);
glVertex3f( 050.0f , 050.0f , -100.0f);
glVertex3f(-050.0f , 050.0f , -100.0f);
glVertex3f(-050.0f , -050.0f , -100.0f);
glVertex3f( 050.0f , -050.0f , -100.0f);
glEnd();
}
void ballMove(){
glPushMatrix();
glColor3f(1,1,0);
b1.pos.x += (b1.pathDirection.x * speed); b1.pos.y += (b1.pathDirection.y * speed); b1.pos.z += (b1.pathDirection.z * speed);
glTranslatef(b1.pos.x,b1.pos.y,b1.pos.z);
glutSolidSphere(b1.radius,100,100);
printf("%.2f %.2f %.2f\n", b1.pos.x, b1.pos.y, b1.pos.z);
glPopMatrix();
planeCollision();
}
void drawPaddle(){
//printf("x %f y %f\n" , mX , mY);
glPushMatrix();
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_QUADS);
glColor3f(1,1,1);
glVertex3f(mX + 25.0f , mY + 25.0f , 0.0f);
glVertex3f(mX + -25.0f , mY + 25.0f , 0.0f);
glVertex3f(mX + -25.0f , mY + -25.0f , 0.0f);
glVertex3f(mX + 25.0f , mY + -25.0f , 0.0f);
glEnd();
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
glPopMatrix();
paddlePlane.max.x=mX + 0.25f; paddlePlane.max.y=mY + 0.25f; paddlePlane.max.z=1;
paddlePlane.min.x=mX + -0.25f; paddlePlane.min.y=mY + -0.25f; paddlePlane.min.z=1;
}
void display()
{
float red[] = {1,0,0,1};
float blue[] = {0,0,1,1};
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_NORMALIZE);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glPushMatrix();
glRotated(g_y_angle, 0, 1, 0);
glRotated(g_x_angle,1,0,0);
glTranslated(0,0,-100);
drawPaddle();
ballMove();
drawPlanes();
glPopMatrix();
angle += 1;
glFlush();
glutSwapBuffers();
}
void reshape(int width, int height)
{
if (height==0)
{
height=1;
}
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.0f,50.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void idle()
{
glutSetWindow(g_mainWindow);
glutPostRedisplay();
}
void variableInits(){
//Floor Plane
pl1.pos.x = 0; pl1.pos.y = -50; pl1.pos.z = 0;
pl1.norm.x = 0; pl1.norm.y = 1; pl1.norm.z = 0;
//Ceiling Plane
pl2.pos.x = 0; pl2.pos.y = 50; pl2.pos.z = 0;
pl2.norm.x = 0; pl2.norm.y = -1; pl2.norm.z = 0;
//Right Wall Plane
pl3.pos.x = 50; pl3.pos.y = 0; pl3.pos.z = 0;
pl3.norm.x = -1; pl3.norm.y = 0; pl3.norm.z = 0;
//Left Wall Plane
pl4.pos.x = -50; pl4.pos.y = 0; pl4.pos.z = 0;
pl4.norm.x = 1; pl4.norm.y = 0; pl4.norm.z = 0;
//Back Wall Plane
pl5.pos.x = 0; pl5.pos.y = 0; pl5.pos.z = -100;
pl5.norm.x = 0; pl5.norm.y = 0; pl5.norm.z = 1;
//Paddle Plane
paddlePlane.max.x=.25; paddlePlane.max.y=.25; paddlePlane.max.z=1;
paddlePlane.min.x=-.25; paddlePlane.min.y=-.25; paddlePlane.min.z=1;
paddlePlane.normals.x=0; paddlePlane.normals.y=0;paddlePlane.normals.z=-0;
//Ball Init
b1.radius = 10;
b1.pathDirection.x = 0; b1.pathDirection.y = 0; b1.pathDirection.z = 0;
b1.pos.x = 0; b1.pos.y = 0, b1.pos.z = -25;
}
int main(int ac, char* av[])
{
glutInit(&ac, av);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
g_mainWindow = glutCreateWindow("Hello, glut");
variableInits();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
glutMouseFunc(mouse);
glutMotionFunc(mouseDrag);
glutPassiveMotionFunc(paddle);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glLightfv(GL_LIGHT0, GL_POSITION, g_lightPos);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glutMainLoop(); //no return
}
Header
#pragma once
#ifndef pHeader_INCLUDED
#define pHeader_H_INCLUDED
typedef struct vector{
float x,y,z;
}vector;
typedef struct ball{
float radius;
vector pathDirection;
vector pos;
} ball;
typedef struct planeStruc{
vector pos;
vector norm;
} plane;
typedef struct paddlePlane{
vector min;
vector max;
vector normals;
} paddlePl;
#endif
gluPerspective cannot have a value of 0.0 for the near plane, it either generates an error (I'm not sure) or causes some weird infinity math to happen that's certainly not what you want.
You should make the value of the near plane greater than zero, and ideally as large as possible without cutting off things that you want to see. If you make it too small you'll lose a ton of z precision.
Try a value of 0.1 to start with.