1
votes

In my experiments using the shader modifier, I saw that the array data could not be transferred to the shader.

Scenekit giving buffer size error while passing array data to uniform array in openGL shader

For this reason I decided to try SCNProgram. But now I realize that the shaders I added using SCNProgram do not work on SCNFloor.

Is there a particular reason for this problem?

Super simple shaders which I use for testing;

vertex shader

precision highp float;
attribute vec3 vertex;
uniform mat4 ModelViewProjectionMatrix;

void main()
{
    gl_Position = ModelViewProjectionMatrix * vec4(vertex, 1.0);
}

fragment shader

precision mediump float;

void main( void )
{   
    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
2
Docs say that SCNFloor is an 'infinite floor'. Your transformation is not going to achieve that, so an assumption can be made that a different vertex shader is running on the floor. Not sure what results you are getting. But lets say that the geometry it works on is a unit quad, just 4 vertices, 2 triangles, transforming it this way could make it into a very finite square living in the center of your scene. Another shader could do some tests and stuff to figure out where these vertices would go to actually make it seem like its infinite (have a horizon and all, not be clipped).pailhead
Amazing to see another person using scene kit. I believe there are like 10 devs in the world now, including the people who worked on it?pailhead
Thanks @pailhead. Yes, I feel so lonely about SceneKit since I was working on it too. :D Yes, maybe it's not a good idea to spend time with this kind of platform depended frameworks. But IMHO, familiar syntax of swift, made SceneKit is joyful and flexible. I feel fun while working on it. And, you are right, SCNFloor has is infinite geometry and probably transformations can not affect on it. But, it must be a way to change color at least. Related to this problem I communicated with apple DTS and sent a sample project file about the problem. I'll share it here when they respond.ytur
i remember the shaders being super buggy, some uniform would be set to some arbitrary values and nothing would be able to change them. edit found it, stackoverflow.com/questions/39855337/…pailhead
BTW, have you tried this? developer.apple.com/reference/scenekit/scnshadable , seems like it would be more likely to work, possibly by just hooking into a stage int he fragment shader.pailhead

2 Answers

1
votes

You can try to make your own vert and frag shaders to kinda do the same thing. I've done a similar thing with webgl / glsl es2, or just paint it solid, tint the existing floor:

uniform mat4 uCameraWM;  //camera world matrix
uniform vec2 uCamAspFov; //x:aspect ratio y:tan(fov/2) 


varying vec3 vVDw; //view direction

void main(){

//construct frustum, scale and transform a unit quad

vec4 viewDirWorld = vec4(
    position.x * uCamAspFov.x,
    position.y,
    -uCamAspFov.y, //move it to where 1:aspect fits
    0.
);

vVDw = ( uCameraWM * viewDirWorld ).xyz; //transform to world


gl_Position = vec4( position.xy , 0. , 1. ); //draw a full screen quad

}

Frag: (ignore the cruft, point is to intersect the view direction ray with a plane, map it, you can look up a cubemap in the sky portion instead of discarding it)

uniform float uHeight;


uniform sampler2D uTexDiff;
uniform sampler2D uTexNorm;
uniform sampler2D uTexMask;

varying vec2 vUv;

varying vec3 vVDw;

struct Plane
{
    vec3 point;
    vec3 normal;
    float d;
};


bool rpi( in Plane p , in vec3 p0 , in vec3 vd , out vec3 wp )
{

    float t;

    t = -( dot( p0 , p.normal ) + p.d ) / dot( vd , p.normal );

    wp = p0 + t * vd;

    return t > 0. ? true : false;

}

void main(){


    Plane plane;

    plane.point = vec3( 0. , uHeight , 0. );
    plane.normal = vec3( 0. , 1. , .0 );
    plane.d = -dot( plane.point , plane.normal );



    vec3 ld = normalize( vec3(1.,1.,1.) );

    vec3 norm = plane.normal;
    float ndl = dot( norm , ld ) ;



    vec2 uv;
    vec3 wp;
    vec3 viewDir = normalize( vVDw );

    vec3 h = normalize((-viewDir + ld));

    float spec = clamp( dot( h , norm ) , .0 , 1. );

    // spec = pow( spec , 5. ); 

    if( dot(plane.normal , cameraPosition) < 0. ) discard;

    if( !rpi( plane , cameraPosition , viewDir , wp ) ) discard;

    uv = wp.xz;

    vec2 uvm = uv * .0105;
    vec2 uvt = uv * .2;

    vec4 tmask = texture2D( uTexMask , uvm );

    vec2 ch2Scale = vec2(1.8,1.8);
    vec2 ch2Scale2 = vec2(1.6,1.6);

    vec2 t1 = uvt * ch2Scale2 - vec2(tmask.z , -tmask.z) ;
    // vec2 t2 = uvt * ch2Scale + tmask.z ;
    // vec2 t3 = uvt + vec2(0. , mask.z-.5) * 1.52;


    vec3 diffLevels = ( texture2D( uTexDiff , t1 ) ).xyz;

    // vec3 diffuse2 = ( texture2D( uTexDiff, fract(t1) * vec2(.5,1.) + vec2(.5,.0) ) ).xyz;
    // vec3 diffuse1 = ( texture2D( uTexDiff, fract(t2) * vec2(.5,1.) ) ).xyz;


    // vec4 normalMap2 = texture2D( uTexNorm, fract(t1) * vec2(.5,1.) + vec2(.5,.0) );
    // vec4 normalMap1 = texture2D( uTexNorm, fract(t2) * vec2(.5,1.) );

    float diffLevel = mix(diffLevels.y, diffLevels.x, tmask.x);
    diffLevel = mix( diffLevel , diffLevels.z, tmask.y );

    // vec3 normalMix = mix(normalMap1.xyz, normalMap2.xyz, tmask.x);


    // vec2 g = fract(uv*.1) - .5;

    // float e = .1;
    // g = -abs( g ) + e;

    float fog = distance( wp.xz , cameraPosition.xz );

    // float r = max( smoothstep( 0.,e,g.x) , smoothstep( 0.,e,g.y) );

    gl_FragColor.w = 1.;

    gl_FragColor.xyz = vec3(tmask.xxx);
    gl_FragColor.xyz = vec3(diffLevel) * ndl + spec * .5;

}

But overall, the better advice would be just to give up on scenekit and save yourself a TON of frustration.

0
votes

Finally Apple Developer Technical Support answered the question I asked about this issue.

Here is the answer that they give.

Unfortunately, there is not a way to shade floor as such. The SceneKit team admits that SCNFloor is a different kind of object that is not intended for use with SCNProgram. Furthermore, using the .fragment shader entry point does not work either (such as:)

func setFragmentEntryPoint( _ node: SCNNode ) {

    print( #function + " setting fragment entry point for \(node)" )

    DispatchQueue.main.asyncAfter( deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds( 2500 ) ) {

        let geometry = node.geometry!

        let dict: [SCNShaderModifierEntryPoint:String] = [.fragment :
            "_output.color = vec4( 0.0, 1.0, 0.0, 1.0 );"]

        geometry.shaderModifiers = dict


    }
}

Though the SceneKit team considered this behavior to be expected, you may still file a bug report which in this case will be interpreted as an API enhancement request.