Mobile: Comment réaliser un effet d'"effacement" d'une texture sur l'écran ?

Questions à propos du scripting Shader.
MichelPeseur
Messages : 1
Inscription : 03 Fév 2018 00:15

Mobile: Comment réaliser un effet d'"effacement" d'une texture sur l'écran ?

Message par MichelPeseur » 03 Fév 2018 01:25

Bonjour,

Tout d'abord je vous prie de m'excuser si mon sujet ne se trouve pas dans la bonne section du forum.
J'utilise Unity depuis 6 mois et si je suis content de la façon dont j'arrive à scripter le gameplay, je me retrouve beaucoup moins à l'aise lorsque je veux réaliser des effets graphiques.

Je souhaiterais réaliser un effet de "nettoyage" de l'écran dans un projet sur plate-forme mobile.
En gros, appliquer une texture disons de sable sur toute la taille de l'écran et proposer à l'utilisateur de nettoyer son écran en effaçant la texture de sable là où il passe son doigt. Je ne pense pas que l'on efface vraiment la texture mais plutôt que l'on y applique un alpha de 0 à l'endroit où l'on passe le doigt. Cet effet me paraît simple dans le principe mais je n'arrive pas à le réaliser correctement.

J'ai dans un premier temps entrepris de le faire pixel par pixel en calculant un cercle autour du pixel cliqué puis en appliquant de l'alpha en fonction de l'éloignement du pixel par rapport au centre mais j'avais de gros soucis de performances, probablement dus au fait que ce n'est pas au CPU de faire ce travail là. Je me suis ensuite penché sur des solutions via shader

J'ai essayé ça:
https://answers.unity.com/questions/449 ... plane.html

Et c'est quelque chose qui s'approche de ce que je veux faire, cependant cela ne modifie l'alpha qu'à l'endroit où le pointeur se trouve et dès lors qu'on se déplace, l'endroit précédent redevient opaque.

J'ai ensuite essayé de faire un tampon que j'applique par dessus ma texture. Voici une partie du code que j'ai utilisé:

Code : Tout sélectionner

using System;
using System.Collections.Generic;
using System.Collections;

using TouchScript;
using UnityEngine;

public class SnowmanManager : MonoBehaviour
{
	private static int CLEAN_RADIUS = 180;

	public Texture2D StampTexture;
	public Texture2D SnowTexture;
	public Texture2D OriginalTexture;
	public Material StampMaterial;

	private bool m_pressed;
	private bool m_touchEnabled = true;
	private RenderTexture m_renderTexture;

	private void Start()
	{
		m_renderTexture = new RenderTexture(
				OriginalTexture.width,
				OriginalTexture.height,
				32
				);

		SnowTexture.GetComponent<Renderer>().material.mainTexture = m_renderTexture;
		RenderTexture.active = m_renderTexture;
		Graphics.Blit(OriginalTexture, m_renderTexture);

		// Version CPU
		/* for (int j = 0; j < m_cleanTexture.height; j++)
		   {
		   for (int i = 0; i < m_cleanTexture.width; i++)
		   {
		   var c = m_cleanTexture.GetPixel(i, j);
		   c.a = 255;
		   m_cleanTexture.SetPixel(i, j, c);
		   }
		   }
		   m_cleanTexture.Apply();*/
	}

	private void OnEnable()
	{
		if (TouchScript.TouchManager.Instance != null)
		{
			TouchScript.TouchManager.Instance.PointersPressed += _onScreenTouched;
			TouchScript.TouchManager.Instance.PointersUpdated += _onScreenUpdated;
			TouchScript.TouchManager.Instance.PointersReleased += _onScreenReleased;
		}
	}

	private void OnDisable()
	{
		if (TouchScript.TouchManager.Instance != null)
		{
			TouchScript.TouchManager.Instance.PointersPressed -= _onScreenTouched;
			TouchScript.TouchManager.Instance.PointersUpdated -= _onScreenUpdated;
			TouchScript.TouchManager.Instance.PointersReleased -= _onScreenReleased;
		}
	}

	private void _onScreenTouched(object sender, PointerEventArgs e)
	{
		if (m_touchEnabled)
		{
			//_cleanScreen(e.Pointers[0].Position, CLEAN_RADIUS);
			_applyStamp(e.Pointers[0].Position);
			m_pressed = true;
		}
		else
		{
			TouchScript.TouchManager.Instance.CancelPointer(0);
		}
	}

	private void _onScreenUpdated(object sender, PointerEventArgs e)
	{
		if (m_touchEnabled)
		{
			if (m_pressed)
			{
				//_cleanScreen(e.Pointers[0].Position, CLEAN_RADIUS);
				_applyStamp(e.Pointers[0].Position);
			}
		}
		else
		{
			TouchScript.TouchManager.Instance.CancelPointer(0);
		}
	}

	private void _onScreenReleased(object sender, PointerEventArgs e)
	{
		m_pressed = false;
	}

#region Clean Screen

	private void _cleanScreen(Vector3 position, int cleanSize)
	{
		RaycastHit hit;
		if (!Physics.Raycast(m_camera.ScreenPointToRay(position), out hit))
			return;

		if (SnowTexture == null)
		{
			Renderer rend = hit.transform.GetComponent<Renderer>();
			SnowTexture = rend.material.mainTexture as Texture2D;
		}
		Vector2 pixelUV = hit.textureCoord;

		pixelUV.x *= SnowTexture.width;
		pixelUV.y *= SnowTexture.height;

		_paintCircle(pixelUV.x, pixelUV.y, cleanSize);
	}

	private void _paintCircle(float x, float y, int radius)
	{
		var center = new Vector2(x, y);
		var side = 2 * radius;

		for (int i = 0 - side; i <= side; ++i)
		{
			for (int j = 0 - side; j <= side; ++j)
			{
				var squarePixel = new Vector2(x + i, y + j);
				var distance = Vector2.Distance(
						squarePixel,
						center
						);

				if (distance <= radius)
				{
					float alpha = 1 - (1 - (distance / radius));

					var xToPaint = (int)squarePixel.x;
					var yToPaint = (int)squarePixel.y;
					var col = SnowTexture.GetPixel(xToPaint, yToPaint);

					if (col.a > alpha)
					{
						col.a = alpha;
						SnowTexture.SetPixel(xToPaint, yToPaint, col);
					}
				}
			}
		}

		SnowTexture.Apply();
	}

	private void _applyStamp(Vector3 position)
	{
		RaycastHit hit;
		if (!Physics.Raycast(Camera.main.ScreenPointToRay(position), out hit))
			return;

		var x = Mathf.Round(hit.textureCoord.x * m_renderTexture.width);
		var y = Mathf.Round(hit.textureCoord.y * m_renderTexture.height);

		Rect screenRect = new Rect();
		screenRect.x = x - (StampTexture.width / 2);
		screenRect.y = (m_renderTexture.height - y) - (StampTexture.height / 2);
		screenRect.width = StampTexture.width;
		screenRect.height = StampTexture.height;

		RenderTexture.active = m_renderTexture;
		GL.PushMatrix();
		GL.LoadPixelMatrix(0, m_renderTexture.width, m_renderTexture.height, 0);

		Graphics.DrawTexture(screenRect, StampTexture, StampMaterial);

		GL.PopMatrix();
		RenderTexture.active = null;
	}

#endregion

J'arrive à faire fonctionner ce tampon mais cependant j'aimerais lui donner la forme que je veux. Pour l'instant si j'utilise le shader standard je parviens à faire une forme carrée (qui correspond je suppose au screenRect) qui "efface" bien ma texture.
Cependant, j'aimerais pouvoir faire une forme circulaire de mon tampon et avoir un alpha progressif des côtés vers le centre. Je pense que tout ça a à voir avec le shader qui est utilisé sur le material du tampon. Mais la programmation de shader me paraît nécessiter des connaissances dans la pipeline de rendu 3D et c'est un peu là que le bât blesse. En réalité, je crois avoir compris à quoi sert un shader mais j'ai du mal à comprendre comment faire ce que je veux dedans ni si ce que je veux y faire est possible.

J'ai énormément cherché, posté des questions sur les forums officiels Unity mais toujours restées sans réponse. Je me dis que j'arriverai certainement à mieux expliquer mon problème dans ma langue maternelle.

Mes questions principales sont donc:
Est-ce que c'est bien la façon de procéder pour réaliser ce genre d'effet ?
Est-ce que c'est bien le shader qui va déterminer le comportement de l'application de ma texture tampon ?
Comment dire dans un shader "appliquer de l'alpha partout où la texture est noire et ne rien changer à la couleur là où la texture est transparente"

Merci d'avance pour votre aide

Répondre

Revenir vers « les Shaders »