Camera-facing UVs

Hi there,

I’ve been trying to replicate Valve’s camera-facing UVs in Unity, but I haven’t been able to so far.

This is the talk about the method, the technical explanation starts at page 74, where you have to build a special matrix in tangent space.

I tried some things using Unity’s TANGENT_SPACE_ROTATION shader macro, but no luck so far!
I lack the maths knowledge to actually understand how the effect works, so if anyone has any insight I’d be very thankful!

Oh and the goal for me is to improve the effect of my cartoon shaders: I’ve got a “sketch effect” that overlays a screen-space texture on top of shadowed areas to simulate hand drawn strokes. The problem is that if the texture is not animated, then you quickly get that “shower door” effect whenever the view moves because the strokes stay in place, so I’d like to find a solution to get rid of that!

2 Likes

I can probably help with that, I already wrote a bit, but first I realized that your first link seems to not go to the talk. So before I digress it is probably best I just read the talk. Can you check that link?

My bad, some clipboard confusion!
The proper paper is linked now :slight_smile:

Well my understanding is that traditional screen-space UVs will just get you, well, screen-space texture coordinate in the sense that a particular point of the texture will always match the same point on your screen.

What I’m looking for, and seems to be achieved by Valve here, is get the texture to be screen-facing but still retain some positioning information with the model it is mapped to.
So that when the view moves, the texture still appears to be attached to that object, rather than looking like an overlay on the whole screen but showing on the object only.

If you’re talking about that, then I’d love some references!

Yeah. So now that I have read the paper, i don’t think this is going to work the way you want it to. :stuck_out_tongue:

So this requires two things, first you need to create the particular uv map that will be transformed.

This may not be as simple as you would like. For a sketchy charcoal like effect you would need a pretty clever UV texture.

But we can leave that part aside, since who knows what that would even necessarily look like for something like this. Who knows, maybe one of those bubble textures may be enough.

Also, since this is done in tangent space, that means it will most likely have problems at UV shell seams. Unless you fade out at those seam areas.

Anyway, unfortunately I can’t pull out the matrix math and the CG program code you’ll need for that right away, which I might try to do at anyrate at somepoint to help someone who might actually need the bubbles and stuff, but I don’t think this will solve your issue.

What you are asking for is a very difficult concept, and I’m not sure I have the best answer. I have some ideas, but most of them arent’ actually all that great. One would be to animate the fullscreen effect so it gives it a every frame hand drawn effect.

Another would be to attempt to map the fullscreen texture using depth aswell for texture scale, though you have to be careful there, but I think it is an interesting idea, though it will still be vaguely screendoorlike.

Thanks for the help!

I’m actually only interested in the “camera facing UVs” part of the paper, not the embedded sprite into a texture: I was curious to see how it looks without that effect actually, because I took a look in Portal 2 directly and actually had trouble to really see what’s going on with the bubbles!

But yeah, what I’m after may technically not be possible. I do have the option to animate the strokes, but I’d still like to pursue the static option, especially because I think it could be used for a lot of other uses.

Other ideas I had include:

  • adjusting the screen-space UVs based on a combination between the camera matrix and the object’s matrix
  • use motion vectors to adjust the UVs
  • use some temporal data, kind of like this coherent noise reasearched by Pixar

But these are pretty vagues ideas, I don’t actually know if they can solve this particular problem (to be honest I’m not exactly sure how the strokes should look on the model: I can easily imagine them when you just move the camera (just scroll the texture on either axis, or scale it for a Z translation), but not when you rotate the camera…)

What I would do is use ‘screen space UVs’ and then add the objects screen based position to it as an extra offset.

Like this :slight_smile:

https://s13.postimg.org/kgm9vjarb/Screen_Aligned_UVs.jpg

2 Likes

That is actually not too bad! Thanks!

http://www.jeanmoreno.com/TEMP/ScreenSpaceOffset.mp4
(left capsule is with corrected offsets, right one shows the “shower door” artefacts)

I wonder how I didn’t think about that before… but why make things simple when you can make them complicated, right? :slight_smile:
I had to adjust with a constant that I think is related to the screen size, but it’s a big step in the right direction!

1 Like

You’re welcome, Jean :slight_smile: Would it surprise you if I was to say that I got the idea from an old Amiga demo, LOL!

3 Likes

Haha awesome! A good reminder that old techs and tricks are always relevant :slight_smile:

LOL! Yep, they certainly are :slight_smile:

So I’ve produced the camera facing UVs in unreal. I will do the same in unity in a bit.

EDIT: I was right, creating the transform matrix in the pixel shader is the cause of the distortion. I’ve modified this post and removed the old info to make it less confusing when searching for this later.

If you get too close the quad there is some distortion, but it is pretty good. UV seams are treated harshly, as are surfaces where the 0-1 UVs lay on a curved surface. Spheres are especially bad. In short the source needs to be flat for the result to look flat.

There may be ways to change this to mitigate that or to ‘counteract’ the curved surface, but i don’t have the math for that. One problem with the sphere I have is that the UVs all the way around the surface. For this to not look janky each face almost should probably have the same UVs, but then you would get crazy seams.

Messing with the sphere’s UVs the closest I was able to get is:

Which is pretty bad.

It is possible that these errors are because of some mistake I’ve made in transposing the talk, but I am not suprised by the types of errors I am seeing.

The code in the custom node:

float2 UV2 = UV - float2(0.5,0.5);

UV.x = dot(M0, UV);
UV.y = dot(M1, UV);

UV += float2( 0.5, 0.5 );

return UV;

The copy pasta of the network:

Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_78"
   Begin Object Class=MaterialExpressionTextureCoordinate Name="MaterialExpressionTextureCoordinate_11"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7328"
   End Object
   Begin Object Name="MaterialExpressionTextureCoordinate_11"
      MaterialExpressionEditorX=-848
      MaterialExpressionEditorY=16
      MaterialExpressionGuid=3C23934A4781FFE6AA26A6BF1C5B8DE5
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7328"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_73.EdGraphPin_7427'
   End Object
   MaterialExpression=MaterialExpressionTextureCoordinate'MaterialExpressionTextureCoordinate_11'
   Pins(0)=EdGraphPin'EdGraphPin_7328'
   NodePosX=-848
   NodePosY=16
   NodeGuid=FC1739FC41CED040908158945F531119
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_61"
   Begin Object Class=MaterialExpressionMaterialFunctionCall Name="MaterialExpressionMaterialFunctionCall_9"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7338"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7339"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7340"
   End Object
   Begin Object Name="MaterialExpressionMaterialFunctionCall_9"
      MaterialFunction=MaterialFunction'/Engine/ArtTools/RenderToTexture/MaterialFunctions/CheckerPattern.CheckerPattern'
      FunctionInputs(0)=(ExpressionInputId=F927958E47707EF9BE57939C3B94F801,Input=(Expression=MaterialExpressionCustom'MaterialGraphNode_73.MaterialExpressionCustom_9',InputName="UVs"))
      FunctionInputs(1)=(ExpressionInputId=6DDA1BD541E6AB14DFB4E78F850A8945,Input=(Expression=MaterialExpressionConstant2Vector'MaterialGraphNode_62.MaterialExpressionConstant2Vector_4',InputName="Tiling"))
      FunctionOutputs(0)=(ExpressionOutputId=7183E0CD4C9C77E63748A3A80E7DB378,Output=(OutputName="Result"))
      MaterialExpressionEditorX=-288
      MaterialExpressionEditorY=112
      MaterialExpressionGuid=61FA14094DD51F2555ED3D800A7F7CF4
      Material=PreviewMaterial'/Engine/Transient.Car'
      Outputs(0)=(OutputName="Result")
   End Object
   Begin Object Name="EdGraphPin_7338"
      PinName="UVs (V2)"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_73.EdGraphPin_7430'
   End Object
   Begin Object Name="EdGraphPin_7339"
      PinName="Tiling (V2)"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_62.EdGraphPin_7341'
   End Object
   Begin Object Name="EdGraphPin_7340"
      PinName="Result"
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_Root_3.EdGraphPin_7297'
   End Object
   MaterialExpression=MaterialExpressionMaterialFunctionCall'MaterialExpressionMaterialFunctionCall_9'
   Pins(0)=EdGraphPin'EdGraphPin_7338'
   Pins(1)=EdGraphPin'EdGraphPin_7339'
   Pins(2)=EdGraphPin'EdGraphPin_7340'
   NodePosX=-288
   NodePosY=112
   NodeGuid=C304BABF47A7151E43F6B1AF11EBCEB8
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_62"
   Begin Object Class=MaterialExpressionConstant2Vector Name="MaterialExpressionConstant2Vector_4"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7341"
   End Object
   Begin Object Name="MaterialExpressionConstant2Vector_4"
      R=10.000000
      G=10.000000
      MaterialExpressionEditorX=-352
      MaterialExpressionEditorY=224
      MaterialExpressionGuid=92D89FF64DE04ADC994AA9AD95A08C21
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7341"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_61.EdGraphPin_7339'
   End Object
   MaterialExpression=MaterialExpressionConstant2Vector'MaterialExpressionConstant2Vector_4'
   Pins(0)=EdGraphPin'EdGraphPin_7341'
   NodePosX=-352
   NodePosY=224
   NodeGuid=7B81B55742CB7114FA398BACD0DE6032
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_65"
   Begin Object Class=MaterialExpressionConstant3Vector Name="MaterialExpressionConstant3Vector_6"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7353"
   End Object
   Begin Object Name="MaterialExpressionConstant3Vector_6"
      Constant=(R=0.000000,G=1.000000,B=0.000000,A=0.000000)
      MaterialExpressionEditorX=-1728
      MaterialExpressionEditorY=252
      MaterialExpressionGuid=0C61787B42828ADF1472658096CC3CE6
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7353"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_68.EdGraphPin_7354'
   End Object
   MaterialExpression=MaterialExpressionConstant3Vector'MaterialExpressionConstant3Vector_6'
   Pins(0)=EdGraphPin'EdGraphPin_7353'
   NodePosX=-1728
   NodePosY=252
   NodeGuid=513EA3A147F18EF91F8C34B2601E1AB4
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_68"
   Begin Object Class=MaterialExpressionTransform Name="MaterialExpressionTransform_11"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7354"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7355"
   End Object
   Begin Object Name="MaterialExpressionTransform_11"
      Input=(Expression=MaterialExpressionConstant3Vector'MaterialGraphNode_65.MaterialExpressionConstant3Vector_6')
      TransformSourceType=TRANSFORMSOURCE_Camera
      TransformType=TRANSFORM_Tangent
      MaterialExpressionEditorX=-1537
      MaterialExpressionEditorY=240
      MaterialExpressionGuid=ABD5FFEB42ADBEEBFC5F2B8FFCFC22CA
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7354"
      PinName="Input"
      PinFriendlyName=" "
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_65.EdGraphPin_7353'
   End Object
   Begin Object Name="EdGraphPin_7355"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_69.EdGraphPin_7361'
   End Object
   MaterialExpression=MaterialExpressionTransform'MaterialExpressionTransform_11'
   Pins(0)=EdGraphPin'EdGraphPin_7354'
   Pins(1)=EdGraphPin'EdGraphPin_7355'
   NodePosX=-1537
   NodePosY=240
   NodeGuid=3D4B61F8419BE7826B0A4784E8F7E669
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_69"
   Begin Object Class=MaterialExpressionCrossProduct Name="MaterialExpressionCrossProduct_6"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7361"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7362"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7363"
   End Object
   Begin Object Name="MaterialExpressionCrossProduct_6"
      A=(Expression=MaterialExpressionTransform'MaterialGraphNode_68.MaterialExpressionTransform_11')
      B=(Expression=MaterialExpressionTransform'MaterialGraphNode_75.MaterialExpressionTransform_12')
      MaterialExpressionEditorX=-1407
      MaterialExpressionEditorY=530
      MaterialExpressionGuid=767CE1D043106DC9DEB89EBDC5C89ADA
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7361"
      PinName="A"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_68.EdGraphPin_7355'
   End Object
   Begin Object Name="EdGraphPin_7362"
      PinName="B"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_75.EdGraphPin_7437'
   End Object
   Begin Object Name="EdGraphPin_7363"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_70.EdGraphPin_7364'
      LinkedTo(1)=EdGraphPin'MaterialGraphNode_71.EdGraphPin_7369'
   End Object
   MaterialExpression=MaterialExpressionCrossProduct'MaterialExpressionCrossProduct_6'
   Pins(0)=EdGraphPin'EdGraphPin_7361'
   Pins(1)=EdGraphPin'EdGraphPin_7362'
   Pins(2)=EdGraphPin'EdGraphPin_7363'
   NodePosX=-1407
   NodePosY=530
   NodeGuid=49FF7C8E4984116C4951B689E464D144
End Object
Begin Object Class=MaterialGraphNode_Comment Name="MaterialGraphNode_Comment_1"
   Begin Object Class=MaterialExpressionComment Name="MaterialExpressionComment_13"
   End Object
   Begin Object Name="MaterialExpressionComment_13"
      SizeX=509
      SizeY=248
      Text="M0"
      MaterialExpressionEditorX=-1778
      MaterialExpressionEditorY=190
      MaterialExpressionGuid=488E6CB3400B903033AC4C93A7D342E7
   End Object
   MaterialExpressionComment=MaterialExpressionComment'MaterialExpressionComment_13'
   NodePosX=-1778
   NodePosY=190
   NodeWidth=509
   NodeHeight=248
   NodeComment="M0"
   NodeGuid=6C1E5C0C4D5F1F9297A3289D302F3EAF
End Object
Begin Object Class=MaterialGraphNode_Comment Name="MaterialGraphNode_Comment_2"
   Begin Object Class=MaterialExpressionComment Name="MaterialExpressionComment_14"
   End Object
   Begin Object Name="MaterialExpressionComment_14"
      SizeX=1120
      SizeY=240
      Text="M2"
      MaterialExpressionEditorX=-2401
      MaterialExpressionEditorY=702
      MaterialExpressionGuid=2ED11D10429330CC826108851B3615F3
   End Object
   MaterialExpressionComment=MaterialExpressionComment'MaterialExpressionComment_14'
   NodePosX=-2401
   NodePosY=702
   NodeWidth=1120
   NodeHeight=240
   NodeComment="M2"
   NodeGuid=42250E3A43FFDF83115DC982B6CCBD68
End Object
Begin Object Class=MaterialGraphNode_Comment Name="MaterialGraphNode_Comment_3"
   Begin Object Class=MaterialExpressionComment Name="MaterialExpressionComment_15"
   End Object
   Begin Object Name="MaterialExpressionComment_15"
      SizeX=177
      SizeY=180
      Text="M\'1"
      MaterialExpressionEditorX=-1457
      MaterialExpressionEditorY=480
      MaterialExpressionGuid=D6862A1642F1CEEA5A4111B4CD067EA3
   End Object
   MaterialExpressionComment=MaterialExpressionComment'MaterialExpressionComment_15'
   NodePosX=-1457
   NodePosY=480
   NodeWidth=177
   NodeHeight=180
   NodeComment="M\'1"
   NodeGuid=D3AC510C4CBFF11BC360689F783F0A56
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_70"
   Begin Object Class=MaterialExpressionCrossProduct Name="MaterialExpressionCrossProduct_7"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7364"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7365"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7366"
   End Object
   Begin Object Name="MaterialExpressionCrossProduct_7"
      A=(Expression=MaterialExpressionCrossProduct'MaterialGraphNode_69.MaterialExpressionCrossProduct_6')
      B=(Expression=MaterialExpressionTransform'MaterialGraphNode_75.MaterialExpressionTransform_12')
      MaterialExpressionEditorX=-1056
      MaterialExpressionEditorY=432
      MaterialExpressionGuid=E85CD57044F4367958028CA1AB2FD24A
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7364"
      PinName="A"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_69.EdGraphPin_7363'
   End Object
   Begin Object Name="EdGraphPin_7365"
      PinName="B"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_75.EdGraphPin_7437'
   End Object
   Begin Object Name="EdGraphPin_7366"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_72.EdGraphPin_7367'
   End Object
   MaterialExpression=MaterialExpressionCrossProduct'MaterialExpressionCrossProduct_7'
   Pins(0)=EdGraphPin'EdGraphPin_7364'
   Pins(1)=EdGraphPin'EdGraphPin_7365'
   Pins(2)=EdGraphPin'EdGraphPin_7366'
   NodePosX=-1056
   NodePosY=432
   ErrorType=1
   ErrorMsg="Missing CrossProduct input B"
   NodeGuid=E6E90C2647D89CF098F5508002C16935
End Object
Begin Object Class=MaterialGraphNode_Comment Name="MaterialGraphNode_Comment_4"
   Begin Object Class=MaterialExpressionComment Name="MaterialExpressionComment_16"
   End Object
   Begin Object Name="MaterialExpressionComment_16"
      SizeX=178
      SizeY=180
      Text="M\'0"
      MaterialExpressionEditorX=-1106
      MaterialExpressionEditorY=382
      MaterialExpressionGuid=790CC9AE4E53ED185FAAD6B1FBFCDE62
   End Object
   MaterialExpressionComment=MaterialExpressionComment'MaterialExpressionComment_16'
   NodePosX=-1106
   NodePosY=382
   NodeWidth=178
   NodeHeight=180
   NodeComment="M\'0"
   NodeGuid=1500995E43A6CC00AEA97B921922A590
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_72"
   Begin Object Class=MaterialExpressionComponentMask Name="MaterialExpressionComponentMask_8"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7367"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7368"
   End Object
   Begin Object Name="MaterialExpressionComponentMask_8"
      Input=(Expression=MaterialExpressionCrossProduct'MaterialGraphNode_70.MaterialExpressionCrossProduct_7')
      R=True
      G=True
      MaterialExpressionEditorX=-720
      MaterialExpressionEditorY=416
      MaterialExpressionGuid=B900F88B461ADEF48C305589739B2998
      Material=PreviewMaterial'/Engine/Transient.Car'
      Desc="SendToCustomUV1"
      bCommentBubbleVisible=True
   End Object
   Begin Object Name="EdGraphPin_7367"
      PinName="Input"
      PinFriendlyName=" "
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_70.EdGraphPin_7366'
   End Object
   Begin Object Name="EdGraphPin_7368"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_Root_3.EdGraphPin_7318'
   End Object
   MaterialExpression=MaterialExpressionComponentMask'MaterialExpressionComponentMask_8'
   Pins(0)=EdGraphPin'EdGraphPin_7367'
   Pins(1)=EdGraphPin'EdGraphPin_7368'
   NodePosX=-720
   NodePosY=416
   NodeComment="SendToCustomUV1"
   bCommentBubbleVisible=True
   NodeGuid=78D5F0814FDF7FAAFD94498008DE0EA0
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_71"
   Begin Object Class=MaterialExpressionComponentMask Name="MaterialExpressionComponentMask_9"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7369"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7370"
   End Object
   Begin Object Name="MaterialExpressionComponentMask_9"
      Input=(Expression=MaterialExpressionCrossProduct'MaterialGraphNode_69.MaterialExpressionCrossProduct_6')
      R=True
      G=True
      MaterialExpressionEditorX=-720
      MaterialExpressionEditorY=608
      MaterialExpressionGuid=B900F88B461ADEF48C305589739B2998
      Material=PreviewMaterial'/Engine/Transient.Car'
      Desc="SendToCustomUV2"
      bCommentBubbleVisible=True
   End Object
   Begin Object Name="EdGraphPin_7369"
      PinName="Input"
      PinFriendlyName=" "
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_69.EdGraphPin_7363'
   End Object
   Begin Object Name="EdGraphPin_7370"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_Root_3.EdGraphPin_7319'
   End Object
   MaterialExpression=MaterialExpressionComponentMask'MaterialExpressionComponentMask_9'
   Pins(0)=EdGraphPin'EdGraphPin_7369'
   Pins(1)=EdGraphPin'EdGraphPin_7370'
   NodePosX=-720
   NodePosY=608
   NodeComment="SendToCustomUV2"
   bCommentBubbleVisible=True
   NodeGuid=6FCA8AC948AC25572EBC7DA5A90AA43C
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_73"
   Begin Object Class=MaterialExpressionCustom Name="MaterialExpressionCustom_9"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7427"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7428"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7429"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7430"
   End Object
   Begin Object Name="MaterialExpressionCustom_9"
      Code="float2 UV2 = UV - float2(0.5,0.5);\r\n\r\nUV.x = dot(M0, UV2);\r\nUV.y = dot(M1, UV2);\r\n\r\nUV += float2( 0.5, 0.5 );\r\n\r\nreturn UV;\r\n"
      OutputType=CMOT_Float2
      Inputs(0)=(InputName="UV",Input=(Expression=MaterialExpressionTextureCoordinate'MaterialGraphNode_78.MaterialExpressionTextureCoordinate_11'))
      Inputs(1)=(InputName="M0",Input=(Expression=MaterialExpressionTextureCoordinate'MaterialGraphNode_79.MaterialExpressionTextureCoordinate_12'))
      Inputs(2)=(InputName="M1",Input=(Expression=MaterialExpressionTextureCoordinate'MaterialGraphNode_58.MaterialExpressionTextureCoordinate_13'))
      MaterialExpressionEditorX=-592
      MaterialExpressionEditorY=80
      MaterialExpressionGuid=DFBE67DE4B8118E6A02E0EB4F8232DEE
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7427"
      PinName="UV"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_78.EdGraphPin_7328'
   End Object
   Begin Object Name="EdGraphPin_7428"
      PinName="M0"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_79.EdGraphPin_7440'
   End Object
   Begin Object Name="EdGraphPin_7429"
      PinName="M1"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_58.EdGraphPin_7442'
   End Object
   Begin Object Name="EdGraphPin_7430"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_61.EdGraphPin_7338'
   End Object
   MaterialExpression=MaterialExpressionCustom'MaterialExpressionCustom_9'
   Pins(0)=EdGraphPin'EdGraphPin_7427'
   Pins(1)=EdGraphPin'EdGraphPin_7428'
   Pins(2)=EdGraphPin'EdGraphPin_7429'
   Pins(3)=EdGraphPin'EdGraphPin_7430'
   NodePosX=-592
   NodePosY=80
   NodeGuid=4CCCC6D14BF15877C6AA8E9837695D42
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_75"
   Begin Object Class=MaterialExpressionTransform Name="MaterialExpressionTransform_12"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7436"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7437"
   End Object
   Begin Object Name="MaterialExpressionTransform_12"
      Input=(Expression=MaterialExpressionMultiply'MaterialGraphNode_83.MaterialExpressionMultiply_1')
      TransformSourceType=TRANSFORMSOURCE_World
      TransformType=TRANSFORM_Tangent
      MaterialExpressionEditorX=-1554
      MaterialExpressionEditorY=798
      MaterialExpressionGuid=012CE0C64E58158B3F56F48EB2FEFF46
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7436"
      PinName="Input"
      PinFriendlyName=" "
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_83.EdGraphPin_7450'
   End Object
   Begin Object Name="EdGraphPin_7437"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_69.EdGraphPin_7362'
      LinkedTo(1)=EdGraphPin'MaterialGraphNode_70.EdGraphPin_7365'
   End Object
   MaterialExpression=MaterialExpressionTransform'MaterialExpressionTransform_12'
   Pins(0)=EdGraphPin'EdGraphPin_7436'
   Pins(1)=EdGraphPin'EdGraphPin_7437'
   NodePosX=-1554
   NodePosY=798
   NodeGuid=7C2704E44F8DD1FE43C341BD071CD65B
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_76"
   Begin Object Class=MaterialExpressionConstant Name="MaterialExpressionConstant_3"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7438"
   End Object
   Begin Object Name="MaterialExpressionConstant_3"
      MaterialExpressionEditorX=-480
      MaterialExpressionEditorY=-128
      MaterialExpressionGuid=6DF5BDCC4DD3349850DA0295C54EEFE0
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7438"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_Root_3.EdGraphPin_7293'
   End Object
   MaterialExpression=MaterialExpressionConstant'MaterialExpressionConstant_3'
   Pins(0)=EdGraphPin'EdGraphPin_7438'
   NodePosX=-480
   NodePosY=-128
   NodeGuid=EABB20FE4375EBBF4758219273E6C265
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_77"
   Begin Object Class=MaterialExpressionConstant Name="MaterialExpressionConstant_4"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7439"
   End Object
   Begin Object Name="MaterialExpressionConstant_4"
      R=0.100000
      MaterialExpressionEditorX=-484
      MaterialExpressionEditorY=-70
      MaterialExpressionGuid=EC10DEA94CB80211CCCA4DBA11411F2E
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7439"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_Root_3.EdGraphPin_7295'
   End Object
   MaterialExpression=MaterialExpressionConstant'MaterialExpressionConstant_4'
   Pins(0)=EdGraphPin'EdGraphPin_7439'
   NodePosX=-484
   NodePosY=-70
   NodeGuid=016B0DCF4085600CD491DD8849EB86A3
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_79"
   Begin Object Class=MaterialExpressionTextureCoordinate Name="MaterialExpressionTextureCoordinate_12"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7440"
   End Object
   Begin Object Name="MaterialExpressionTextureCoordinate_12"
      CoordinateIndex=1
      MaterialExpressionEditorX=-848
      MaterialExpressionEditorY=112
      MaterialExpressionGuid=3C23934A4781FFE6AA26A6BF1C5B8DE5
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7440"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_73.EdGraphPin_7428'
   End Object
   MaterialExpression=MaterialExpressionTextureCoordinate'MaterialExpressionTextureCoordinate_12'
   Pins(0)=EdGraphPin'EdGraphPin_7440'
   NodePosX=-848
   NodePosY=112
   NodeGuid=A359F1F74B3A25F016C02AAFEDEEC151
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_58"
   Begin Object Class=MaterialExpressionTextureCoordinate Name="MaterialExpressionTextureCoordinate_13"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7442"
   End Object
   Begin Object Name="MaterialExpressionTextureCoordinate_13"
      CoordinateIndex=2
      MaterialExpressionEditorX=-848
      MaterialExpressionEditorY=192
      MaterialExpressionGuid=3C23934A4781FFE6AA26A6BF1C5B8DE5
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7442"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_73.EdGraphPin_7429'
   End Object
   MaterialExpression=MaterialExpressionTextureCoordinate'MaterialExpressionTextureCoordinate_13'
   Pins(0)=EdGraphPin'EdGraphPin_7442'
   NodePosX=-848
   NodePosY=192
   NodeGuid=A023C4224D9CBAB3DABC9B98CFA977BA
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_81"
   Begin Object Class=MaterialExpressionWorldPosition Name="MaterialExpressionWorldPosition_1"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7445"
   End Object
   Begin Object Name="MaterialExpressionWorldPosition_1"
      WorldPositionShaderOffset=WPT_CameraRelativeNoOffsets
      MaterialExpressionEditorX=-2352
      MaterialExpressionEditorY=800
      MaterialExpressionGuid=0489FAEA498B435A1A12E584DFC40446
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7445"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_82.EdGraphPin_7446'
   End Object
   MaterialExpression=MaterialExpressionWorldPosition'MaterialExpressionWorldPosition_1'
   Pins(0)=EdGraphPin'EdGraphPin_7445'
   NodePosX=-2352
   NodePosY=800
   NodeGuid=2F8C9D6B4F8C01A0B81D4AB203E46277
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_82"
   Begin Object Class=MaterialExpressionNormalize Name="MaterialExpressionNormalize_2"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7446"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7447"
   End Object
   Begin Object Name="MaterialExpressionNormalize_2"
      VectorInput=(Expression=MaterialExpressionWorldPosition'MaterialGraphNode_81.MaterialExpressionWorldPosition_1')
      MaterialExpressionEditorX=-1856
      MaterialExpressionEditorY=800
      MaterialExpressionGuid=ACCBCF3942CBF9BDF2C1C198E485C05F
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7446"
      PinName="VectorInput"
      PinType=(PinCategory="required")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_81.EdGraphPin_7445'
   End Object
   Begin Object Name="EdGraphPin_7447"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_83.EdGraphPin_7448'
   End Object
   MaterialExpression=MaterialExpressionNormalize'MaterialExpressionNormalize_2'
   Pins(0)=EdGraphPin'EdGraphPin_7446'
   Pins(1)=EdGraphPin'EdGraphPin_7447'
   NodePosX=-1856
   NodePosY=800
   NodeGuid=922DD73D4A546B4AADBF5E9F9FE16B68
End Object
Begin Object Class=MaterialGraphNode Name="MaterialGraphNode_83"
   Begin Object Class=MaterialExpressionMultiply Name="MaterialExpressionMultiply_1"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7448"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7449"
   End Object
   Begin Object Class=EdGraphPin Name="EdGraphPin_7450"
   End Object
   Begin Object Name="MaterialExpressionMultiply_1"
      A=(Expression=MaterialExpressionNormalize'MaterialGraphNode_82.MaterialExpressionNormalize_2')
      ConstB=-1.000000
      MaterialExpressionEditorX=-1696
      MaterialExpressionEditorY=800
      MaterialExpressionGuid=58B6458549E44D4EF6BF5C969E9CB59B
      Material=PreviewMaterial'/Engine/Transient.Car'
   End Object
   Begin Object Name="EdGraphPin_7448"
      PinName="A"
      PinType=(PinCategory="optional")
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_82.EdGraphPin_7447'
   End Object
   Begin Object Name="EdGraphPin_7449"
      PinName="B"
      PinType=(PinCategory="optional")
   End Object
   Begin Object Name="EdGraphPin_7450"
      PinName="Output"
      PinFriendlyName=" "
      Direction=EGPD_Output
      LinkedTo(0)=EdGraphPin'MaterialGraphNode_75.EdGraphPin_7436'
   End Object
   MaterialExpression=MaterialExpressionMultiply'MaterialExpressionMultiply_1'
   Pins(0)=EdGraphPin'EdGraphPin_7448'
   Pins(1)=EdGraphPin'EdGraphPin_7449'
   Pins(2)=EdGraphPin'EdGraphPin_7450'
   NodePosX=-1696
   NodePosY=800
   NodeGuid=1CD7A8644B9205BBAE1B70859832ECA0
End Object

As an aside, one thing to keep in mind about the method you have on the capsules, is that if you have deformation on a per-object basis you will still see the screendoor effect. As long as your things move as whole objects you should have no screendoor.

3 Likes

Ooohh, i see. I did interpret your question backwards, sorry!

I’ve edited and updated the post above, so as to not make it more confusing when searching later. Functionally I’ve got the Unreal Implementation up and running, although albiet it really only works well on flat surfaces, which is kinda what I expected.

The unity implementation gave me a bit of trouble last night, but I’m pretty close. I should have it soonish.

With that in mind, I am almost certain this won’t be the effect that @JeanMoreno will want. It still ‘slides’ as the camera or object moves, it just instead of sliding translationally it seems to rotate. :stuck_out_tongue: Still it seems like a valuable thing to have and this is the hard half. I really like these sprite bubbles.

1 Like

Yeah that’s probably not the ultimate solution I was imagining for my problem, but I’m still curious to see that working in motion, and who knows maybe find some other usage for it!

Also it makes sense that if there is object-deformation then the screen-space offset thing then there’ll be heavy artefacts. I actually did try the method @Mike_Oakley suggested before, but I was doing it per-vertex instead of per-object so it was not working at all!

Anyway thanks for the research and the help! :thumbsup:

I made a shader a while ago to do what I think you are asking in Unity:

So it moves in the object’s space and always faces the camera. It pretty much the same think Mike did, take Screen Space UVs and offset by the object position in screen space. I also added scaling of the texture based on distance from the camera so that it was stable for things that move in the z-direction.

Here’s the basic shader:

Shader "Custom/ScreenSpace" {
 Properties {
  _MainTex ("Color Texture", 2D) = "white" {}     
  _SSUVScale("UV Scale", Range(0,10)) = 1
}

CGINCLUDE
	sampler2D _MainTex;
	float _SSUVScale;

	struct appdata {
		float4 vertex : POSITION;
	};

	struct v2f {
		float4 pos : POSITION;
	};

	float2 GetScreenUV(float2 clipPos, float UVscaleFactor)
	{
		float4 SSobjectPosition = mul (UNITY_MATRIX_MVP, float4(0,0,0,1.0)) ;
		float2 screenUV = float2(clipPos.x/_ScreenParams.x,clipPos.y/_ScreenParams.y);
		float screenRatio = _ScreenParams.y/_ScreenParams.x;

		screenUV.y -=0.5;
		screenUV.x -=0.5;

		screenUV.x -= SSobjectPosition.x/(2*SSobjectPosition.w);
		screenUV.y += SSobjectPosition.y/(2*SSobjectPosition.w); //switch sign depending on camera
		screenUV.y *= screenRatio;

		screenUV *= 1/UVscaleFactor;
		screenUV *= SSobjectPosition.z;

		return screenUV;
	};


ENDCG

SubShader {
  	Tags { "RenderType" = "Opaque" }

  	Pass 
	{
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#include "UnityCG.cginc"

		v2f vert(appdata v) {				
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

			return o;
		}		

		half4 frag(v2f i) :COLOR 
		{ 				
			float2 screenUV = GetScreenUV(i.pos.xy, _SSUVScale);
			half4 screenTexture = tex2D (_MainTex, screenUV);

			return screenTexture; 
		} 
		ENDCG				 
	}

} 
Fallback "Diffuse"

}

There’s some weird stuff in unity where the viewport camera has different camera constants than the in game cameras, and some of the camera properties are also not available if you’re doing surface shaders, so it can be tricky to tune this just right for all cases

7 Likes

Thanks for sharing!
I was also viewing the texture scrolling when you rotate the object in my head, but that’s less important than when you move the view/object!

I’ve finally implemented this in my shaders, but I did it a bit differently since I’m using surface shaders (and thus don’t have clip space position from the vertex shader there, so it felt redundant to recalculate it).
Here’s the code:

In Vertex Shader:

o.screenPos = ComputeScreenPos(pos);
o.screenPos.xy = TRANSFORM_TEX(o.sketchUv, _SketchTex);

In Fragment Shader:

float2 screenUV = IN.sketchUv.xy / IN.sketchUv.w;
float screenRatio = _ScreenParams.y / _ScreenParams.x;
screenUV.y *= screenRatio;
ObjSpaceUVOffset(screenUV, screenRatio);

ObjSpaceUVOffset function:

inline void ObjSpaceUVOffset(inout float2 screenUV, in float screenRatio)
{
	float4 objPos = float4(UNITY_MATRIX_MVP[0].w, UNITY_MATRIX_MVP[1].w, UNITY_MATRIX_MVP[2].w, UNITY_MATRIX_MVP[3].w);
	
	float offsetFactorX = 0.5;
	float offsetFactorY = offsetFactorX * screenRatio;
	offsetFactorX *= _SketchTex_ST.x;
	offsetFactorY *= _SketchTex_ST.y;
	
	//don't scale with orthographic camera
	if (unity_OrthoParams.w < 1)
	{
		//adjust uv scale
		screenUV -= float2(offsetFactorX, offsetFactorY);
		screenUV *= objPos.z;	//scale with cam distance
		screenUV += float2(offsetFactorX, offsetFactorY);
	}
	
	screenUV.x -= objPos.x * offsetFactorX;
	screenUV.y -= objPos.y * offsetFactorY;
}

I’m not sure if it’s the fastest way, but it works and uses the texture’s tiling/offset values from the material.

I got into this dude (Maybe he is between us) Dan Moran. He got some nice study cases and I highly recommend everyone to see those concepts.

4 Likes

I’m sorry to bump an old thread but I could use some help with this too. I’m trying to create an effect like Arthur did, where the screen space uv is scaled and translated based on the object’s world position relative to the camera. I have scaling based on the distance between the camera and the object itself, but I can’t figure out how to make the uv translate based on the object position.
Here’s my material graph for the screen space uvs so far.

Again I’m sorry to bump an older topic but this has me stumped. Thank you for any help.