By Jason


2019-03-11 20:29:39 8 Comments

In this game I'm making, I want my players to be able to remove rust from an object based on collisions from another object such as a wire brush. Since it's a VR game, performance is key.

I've been having a hard time getting on the right track; I think I'm not asking the right questions. The closest I've gotten was a video about making a scratch-off lotto ticket. Someone in the comments to that video says:

Good Idea. But, this isn't practical for any real world apps. It instantiates too many gameobjects. There are better more efficient ways of achieving this effect using shaders or render texture.

They don't really expand on that further, but I did do a little more searching to see how I might do this using shaders. I found this video about using replacement shaders, but I'm still not sure if that's the right track. I don't know If something like that is the smartest way to do it.

I'm basically a complete noob when it comes to shaders and textures so this is new territory for me. I apologize if this is a duplicate question, I've tried searching but haven't found anything quite like what I'm looking for. Please let me know if there's anything I can explain further, and any help that gets me pointed in the right direction would be greatly appreciated.

1 comments

@Ruther Rendommeleigh 2019-03-12 11:36:43

If you're okay with mesh colliders, take a look at this. The example describes a (primitive) version of what you want.

The next step would probably be to write a shader that takes three textures: a "rusty" version, a "clean" texture and a "splatmap" that stores where to apply which texture. Instead of the main texture, the script should paint into this splatmap.

All the shader needs to do is "fade" between the two textures. I'm at work right now and can't give you all the boilerplate from memory, but the relevant line should look something like this:

out.rgba = _rusty.rgba * _splat.r + _clean.rgba * (1.0 - _splat.r)

I'm using the splatmap's red channel here, any other will do as well. The splatmap can have a different resolution than your other textures so play around with that and pick whatever looks best on your object.

EDIT: Here's the final code that did the trick (made by OP). DotClick.cs should be applied to the camera and ClearableShader should be applied to the object in question.

ClearableShader.shader:

Shader "Custom/ClearableShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _CleanTex("Clean Texture", 2D) = "white" {}
        _RustTex("Rust Texture", 2D) = "white"
        _SplatTex ("Splat Map", 2D) = "white"
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _CleanTex;
        sampler2D _RustTex;
        sampler2D _SplatTex;

        struct Input
        {
            float2 uv_CleanTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 rustTex = tex2D(_CleanTex, IN.uv_CleanTex);
            fixed4 cleanTex = tex2D(_RustTex, IN.uv_CleanTex);
            fixed4 splatTex = tex2D(_SplatTex, IN.uv_CleanTex);

            o.Albedo = (rustTex.rgba * splatTex.r) + (cleanTex.rgba * (1.0 - splatTex.r));
        }
        ENDCG
    }
    FallBack "Diffuse"
}

DotClick.cs

// Write black pixels onto the GameObject that is located
// by the script. The script is attached to the camera.
// Determine where the collider hits and modify the texture at that point.
//
// Note that the MeshCollider on the GameObject must have Convex turned off. This allows
// concave GameObjects to be included in collision in this example.
//
// Also to allow the texture to be updated by mouse button clicks it must have the Read/Write
// Enabled option set to true in its Advanced import settings.

using UnityEngine;
using System.Collections;

public class DotClick : MonoBehaviour
{
    public Camera cam;

    void Start()
    {
        cam = GetComponent<Camera>();
    }

    void Update()
    {
        if (!Input.GetMouseButton(0))
            return;

        RaycastHit hit;
        if (!Physics.Raycast(cam.ScreenPointToRay(Input.mousePosition), out hit))
            return;

        Renderer rend = hit.transform.GetComponent<Renderer>();
        MeshCollider meshCollider = hit.collider as MeshCollider;

        if (meshCollider == null|| rend == null || rend.sharedMaterial == null || rend.material.GetTexture("_SplatTex") == null)
            return;

        Texture2D tex = rend.material.GetTexture("_SplatTex") as Texture2D;
        Vector2 pixelUV = hit.textureCoord;
        pixelUV.x *= tex.width;
        pixelUV.y *= tex.height;

        Circle(tex, (int)pixelUV.x, (int)pixelUV.y, 10, Color.red);
        // tex.SetPixel((int)pixelUV.x, (int)pixelUV.y, Color.red);
        tex.Apply();
    }

    // Taken from https://answers.unity.com/questions/590469/drawing-a-solid-circle-onto-texture.html
    public void Circle(Texture2D tex, int cx, int cy, int radius, Color color)
    {
        int x, y, px, nx, py, ny, d;

        for (x = 0; x <= radius; x++)
        {
            d = (int)Mathf.Ceil(Mathf.Sqrt(radius * radius - x * x));

            for (y = 0; y <= d; y++)
            {
                px = cx + x;
                nx = cx - x;
                py = cy + y;
                ny = cy - y;

                tex.SetPixel(px, py, color);
                tex.SetPixel(nx, py, color);

                tex.SetPixel(px, ny, color);
                tex.SetPixel(nx, ny, color);
            }
        }
    }
}

@Ruther Rendommeleigh 2019-03-12 11:43:03

Hmm. This might be a bit too terse, but I hope it'll point you in the right direction. Feel free to ask followup questions and I'll expand the answer when I'm home.

@Jason 2019-03-12 14:31:08

Awesome, thanks for your reply. I'll take a crack at it and let you know what questions come up :)

@Jason 2019-03-12 19:13:36

First stupid question, what do you mean by "okay with mesh colliders"? Do you mean as opposed to things like box colliders or rolling my own collision detection system?

@Ruther Rendommeleigh 2019-03-13 11:21:30

Mesh colliders, especially complex ones, can be noticeably slower than simple box, capsule or compound colliders. They're often more accurate, but if you use a large number of them, performance may suffer.

@Jason 2019-03-14 04:14:44

Gotcha, that's what I thought. I think I should be fine with mesh colliders because I envision the player mainly interacting with a small number of high detail items. One sword, one tool; good hitboxes will be paramount, so mesh colliders are (probably) the way to go.

@Jason 2019-03-18 04:52:40

I got it all working :) I edited your answer with my final scripts and marked it as solved. Let me know if you have any improvements you'd make, but it looks like it's working great to me. Thanks for your help!

Related Questions

Sponsored Content

1 Answered Questions

[SOLVED] Run Simple HLSL Pixel Shader with DirectX 9

2 Answered Questions

[SOLVED] Creating a master server for Unity game (Unity 2017)

0 Answered Questions

How to create a 2d jelly/soft/blob body?

  • 2018-07-08 17:26:36
  • Jordy Langen
  • 630 View
  • 2 Score
  • 0 Answer
  • Tags:   unity 2d

0 Answered Questions

LibGdx: Texturing cube results in jagged transparent edges

1 Answered Questions

0 Answered Questions

How to apply blur effect to 2D textures using shaders?

  • 2017-04-02 10:13:44
  • user1306322
  • 663 View
  • 0 Score
  • 0 Answer
  • Tags:   shaders monogame

2 Answered Questions

[SOLVED] How can I use shaders to make a square have a waving effect?

1 Answered Questions

[SOLVED] Paint with soft brush (logic)

  • 2014-01-29 12:56:55
  • alaslipknot
  • 2661 View
  • 2 Score
  • 1 Answer
  • Tags:   unity textures

1 Answered Questions

2 Answered Questions

[SOLVED] Compatibility between DirectX 9 and DirectX 10 shaders

Sponsored Content