[RESOLU]Crayonner pour révéler un dessin
[RESOLU]Crayonner pour révéler un dessin
Bonjour à tous,
C'est vrai que mon titre n'est pas clair, mais je voudrais arriver à créer l'effet quand je gratte avec ma souris sur une feuille de papier, ça révèle petit à petit le dessin gravé. Comme si je crayonnais pour noircir la feuille.
Une image peut-être, pour être plus clair
Est-ce que quelqu'un peut me mettre sur la voie pour obtenir un effet semblable.
Merci à tous
C'est vrai que mon titre n'est pas clair, mais je voudrais arriver à créer l'effet quand je gratte avec ma souris sur une feuille de papier, ça révèle petit à petit le dessin gravé. Comme si je crayonnais pour noircir la feuille.
Une image peut-être, pour être plus clair
Est-ce que quelqu'un peut me mettre sur la voie pour obtenir un effet semblable.
Merci à tous
Dernière édition par EmileF le 24 Déc 2019 13:52, édité 1 fois.
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: Crayonner pour révéler un dessin
Salut,
Tu créés une texture qui va contenir ton dessin dans les canaux RGB, et avec l'alpha à zéro.
Puis tu fais une fonction qui va servir à dessiner uniquement dans la couche alpha.
Plus "simple" : tu fais ça avec deux textures, une pour ton dessin et l'autre qui sert de masque (dans lequel du dessine en noir et blanc pour définir les zones opaques), et là il faudra passer par un shader pour gérer le masque d'une texture par l'autre.
Tu créés une texture qui va contenir ton dessin dans les canaux RGB, et avec l'alpha à zéro.
Puis tu fais une fonction qui va servir à dessiner uniquement dans la couche alpha.
Plus "simple" : tu fais ça avec deux textures, une pour ton dessin et l'autre qui sert de masque (dans lequel du dessine en noir et blanc pour définir les zones opaques), et là il faudra passer par un shader pour gérer le masque d'une texture par l'autre.
Re: Crayonner pour révéler un dessin
Merci Alesk pour ta réponse
Je pensais bien que la solution était dans les shaders, mais je ne les maîtrise vraiment pas et je ne saurais même pas par quel bout commencer.
J'ai essayé sur le web mais je ne suis pas arrivé à trouver un sembant d'explication. Si tu avais une explication ou un tuto, j'aimerai bien changer ma solution.
En tout cas, merci
En hébreux, pour moi, ça aurai été plus clair. Je ne vois absolument pas comment faire une fonction qui me permettrait de dessiner uniquement dans la couche alpha. Une petite précision ou un petit tuto serait bienvenu.Alesk a écrit :Tu créés une texture qui va contenir ton dessin dans les canaux RGB, et avec l'alpha à zéro.
Puis tu fais une fonction qui va servir à dessiner uniquement dans la couche alpha.
Là c'est du grec. Mais j'ai trouvé une solution qui s'approche un peu de ce conseil. J'ai 2 textures, un le crayonnage, par dessus mon dessin au travers duquel on voit le crayonnage dans les parties transparentes, et par dessus un cache dont je diminue le scale en grattant avec la souris. Ce n'est pas ce que je voulais mais c'est la seule solution que j'ai trouvé.Alesk a écrit :"Plus "simple" : tu fais ça avec deux textures, une pour ton dessin et l'autre qui sert de masque (dans lequel du dessine en noir et blanc pour définir les zones opaques), et là il faudra passer par un shader pour gérer le masque d'une texture par l'autre.
Je pensais bien que la solution était dans les shaders, mais je ne les maîtrise vraiment pas et je ne saurais même pas par quel bout commencer.
J'ai essayé sur le web mais je ne suis pas arrivé à trouver un sembant d'explication. Si tu avais une explication ou un tuto, j'aimerai bien changer ma solution.
En tout cas, merci
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: Crayonner pour révéler un dessin
Fais un tour ici : https://unitylist.com/p/e5y/Reveal-Shader
Re: Crayonner pour révéler un dessin
Merci Alesk.
Ce site à l'air d'être une source énorme de renseignements.
Je vais jeter un coup d’œil à tout ça.
Je me demande comment vous arrivez à trouver des sources pareilles. J'ai beau chercher je tombe toujours sur le même genre de renseignements basiques ou incomplets.
Merci beaucoup Alesk, j'ai du boulot
Ce site à l'air d'être une source énorme de renseignements.
Je vais jeter un coup d’œil à tout ça.
Je me demande comment vous arrivez à trouver des sources pareilles. J'ai beau chercher je tombe toujours sur le même genre de renseignements basiques ou incomplets.
Merci beaucoup Alesk, j'ai du boulot
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: Crayonner pour révéler un dessin
Le secret est dans les mots clés qu'on utilise pour faire les recherches
Et pour info, je ne connaissais pas non plus ce site... Je l'ai découvert il y a deux jours !
Bon courage !
Re: Crayonner pour révéler un dessin
Je pense que c'est dû à ce satané anglais pour lequel j'ai toujours été allergiqueAlesk a écrit :Le secret est dans les mots clés qu'on utilise pour faire les recherches
Merci Alesk
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: Crayonner pour révéler un dessin
Bien, pas mal, le conseil d'Alesk, je suis arrivé à obtenir a peu près ce que je voulais grâce à lui.
Cependant un petit détail m'irrite un peu. Au passage du curseur sur ma feuille, j'ai des taches rondes qui se succèdent et elles grossissent si le curseur ne bouge plus. J'aurais préféré avoir des traits, plus ou moins régulier et qui restent fixe.
Voilà ce que j'obtiens:
Voilà les scripts que j'utilise:
le Painter:
C'est moi qui ai ajouté les valeurs v1, v2, v3, v4 pour avoir une possibilité de réglage. Les valeurs d'origine sont dans la ligne qui est commentée.
Voila le shader qui est utilisé:
J'ai réglé la propriété Tesselation = 1 et la propriété Displacement à -1;
Est-ce que quelqu'un peut me dire si c'est réalisable d'avoir un trait plus ou régulier et continu au lieu de tache, et quand le curseur reste fixe, la marque reste fixe.
Et si c'est possible, un petit tuyau pour y arriver.
Merci
Cependant un petit détail m'irrite un peu. Au passage du curseur sur ma feuille, j'ai des taches rondes qui se succèdent et elles grossissent si le curseur ne bouge plus. J'aurais préféré avoir des traits, plus ou moins régulier et qui restent fixe.
Voilà ce que j'obtiens:
Voilà les scripts que j'utilise:
le Painter:
Code : Tout sélectionner
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Runningtap
{
[RequireComponent(typeof(BoxCollider))]
public class Painter : MonoBehaviour
{
public enum Mode
{
auto,
manual
}
public enum Resolution
{
_32 = 32, _64 = 64, _128 = 128, _256 = 256, _512 = 512, _1024 = 1024, _2048 = 2048
}
[Tooltip("Le mode Auto va itérer à travers tous les objets pour trouver les limites. Le mode manuel utilisera le collisionneur de boîtes attaché comme limites du monde. Utilisez le manuel pour les scènes complexes avec beaucoup d'objets.")]
public Mode LookupMode = Mode.auto;
public Resolution SplatResolution = Resolution._256;
[Tooltip("Splat Rapport de résolution en fonction du rapport largeur/longueur du monde. Le côté le plus long utilisera la résolution sélectionnée. Aide à minimiser l'étirement du projeciton.")]
public bool UseRelative;
public bool FadeOverTime;
public Transform Brush;
[Range(0, 1)]
public float BrushSize = 1;
[Range(0, 1)]
public float BrushStrength = 1;
[Range(0, 1)]
public float FadeSpeed = 1;
public GameObject World;
public BoxCollider ManualBounds;
public Shader RenderTextureShader;
private Bounds WorldBounds = new Bounds(Vector3.zero, Vector3.zero);
private Vector4 shaderInfo; //XY = Bounds worldspace offset, ZW = Bounds scale.
private RenderTexture splatmap;
private int width;
private int height;
private Material drawMaterial;
private Renderer[] renderers;
//Shader Strings
private const string rtxCoord = "_Coordinate";
private const string rtxStrength = "_Strength";
private const string rtxSize = "_Size";
private const string rtxFade = "_Fade";
private const string revColor = "_Color";
private const string revRTX = "_Splat";
private const string revRTXLocaiton = "_SplatLocation";
private const string revRTXRemap = "_SplatRemap";
public RenderTexture DebugRenderTexture
{
get { return splatmap; }
}
void Start()
{
ManualBounds = GetComponent<BoxCollider>();
renderers = World.GetComponentsInChildren<Renderer>();
drawMaterial = new Material(RenderTextureShader);
drawMaterial.SetVector(revColor, Color.red);
CalculateBounds();
CalculateResolution();
splatmap = new RenderTexture(width, height, 0, RenderTextureFormat.ARGBFloat);
SetShaders();
ManualBounds.enabled = false; // Le collisionneur de boîtes n'est nécessaire que dans l'éditeur pour calculer les limites. Le desactiver pour retirer la physique.
}
// Calculer les limites des objets à peindre
void CalculateBounds()
{
if(LookupMode == Mode.auto)
{
foreach (var entity in renderers)
WorldBounds.Encapsulate(entity.bounds);
if (ManualBounds != null) // Collisionneur de boîtes de forme pour visualiser les limites en mode automatique.
{
ManualBounds.center = WorldBounds.center;
ManualBounds.size = WorldBounds.extents * 2;
}
}
else
WorldBounds = ManualBounds.bounds;
shaderInfo = new Vector4
(
WorldBounds.center.x - WorldBounds.size.x / 2,
WorldBounds.center.z - WorldBounds.size.z / 2,
WorldBounds.size.x,
WorldBounds.size.z
);
}
// Calculer le rapport de résolution de Splatmap s'il est défini sur UseRelative.
void CalculateResolution()
{
if (UseRelative)
{
if (WorldBounds.size.x > WorldBounds.size.z)
{
var ratio = WorldBounds.size.z / WorldBounds.size.x;
width = (int)SplatResolution;
height = Mathf.RoundToInt(width * ratio);
}
else
{
var ratio = WorldBounds.size.x / WorldBounds.size.z;
height = (int)SplatResolution;
width = Mathf.RoundToInt(height * ratio);
}
}
else
{
width = (int)SplatResolution;
height = (int)SplatResolution;
}
}
// Mise à jour des shaders dessinés.
void SetShaders()
{
foreach (var entity in renderers)
{
if (entity.material.HasProperty(revRTX))
{
entity.material.SetTexture(revRTX, splatmap);
entity.material.SetVector(revRTXLocaiton, shaderInfo);
entity.material.SetVector(revRTXRemap, shaderInfo);
}
else
Debug.Log("GameObject <color=yellow>" + entity.gameObject.name + "</color> does not have the reveal shader applied, skipped.", entity);
}
}
void Update()
{
/* Enveloppez ceci dans toute autre fonctionnalité. (par exemple lorsque l'objet Brosse touche le sol, etc.).
* Evitez de redessiner la texture de rendu si vous n'avez pas à mettre à jour le splat map. */
DoPaint();
}
// Mise à jour du Splat Map en fonction de la position de l'objet Brush
public void DoPaint()
{
Vector4 worldToRtx = new Vector4(
map(Brush.transform.position.x, WorldBounds.center.x - WorldBounds.size.x / 2, WorldBounds.center.x + WorldBounds.size.x / 2, 0, 1),
map(Brush.transform.position.z, WorldBounds.center.z - WorldBounds.size.z / 2, WorldBounds.center.z + WorldBounds.size.z / 2, 0, 1),
0,
0
);
drawMaterial.SetVector(rtxCoord, worldToRtx);
drawMaterial.SetFloat(rtxStrength, BrushStrength);
//drawMaterial.SetFloat(rtxSize, map(BrushSize, 0, 1, 1, 0));
drawMaterial.SetFloat(rtxSize, map(BrushSize, v1, v2, v3, v4));
drawMaterial.SetFloat(rtxFade, FadeSpeed);
RenderTexture temp = RenderTexture.GetTemporary(splatmap.width, splatmap.height, 0, RenderTextureFormat.ARGBFloat);
Graphics.Blit(splatmap, temp);
Graphics.Blit(temp, splatmap, drawMaterial);
RenderTexture.ReleaseTemporary(temp);
}
[Range(0,1)]
public float v1 = 0.49f;
[Range(0, 1)]
public float v2 = 0.51f;
[Range(0, 1)]
public float v3 = 0.51f;
[Range(0, 1)]
public float v4 = 0.49f;
// Remplacer un nombre dans une plage par une autre plage
float map(float s, float a1, float a2, float b1, float b2)
{
return b1 + (s - a1) * (b2 - b1) / (a2 - a1);
}
// Calculer le ratio de la taille du monde pour un Splatmap non carré
float ratio(float resolution)
{
float r;
if (WorldBounds.size.x > WorldBounds.size.z)
{
r = WorldBounds.size.z / WorldBounds.size.x;
}
else
r = WorldBounds.size.x / WorldBounds.size.z;
return r;
}
}
}
Voila le shader qui est utilisé:
Code : Tout sélectionner
Shader "Runningtap/Reveal/Displacement" {
Properties
{
_Tess ("Tessellation", Range(1,32)) = 4
_Displacement ("Displacement", Range(-1.0, 1.0)) = 0.3
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Color2 ("Color2", Color) = (1,1,1,1)
_Splat ("SplatMap", 2D) = "black" {}
_SplatRemap ("SplatOffset", Vector) = (0,0,0,0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 300
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:disp tessellate:tessDistance
#pragma target 4.6
#include "Tessellation.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
float2 texcoord2 : TEXCOORD2;
};
struct Input
{
float2 uv_MainTex;
float3 worldPos;
};
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(sampler2D, _MainTex)
UNITY_DEFINE_INSTANCED_PROP(sampler2D, _Splat)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color2)
UNITY_DEFINE_INSTANCED_PROP(float4, _SplatRemap)
UNITY_DEFINE_INSTANCED_PROP(half, _Tess)
UNITY_DEFINE_INSTANCED_PROP(half, _Displacement )
UNITY_INSTANCING_BUFFER_END(Props)
void disp (inout appdata v)
{
float2 WUV = (mul (unity_ObjectToWorld, v.vertex).xz - _SplatRemap.xy)/ _SplatRemap.zw;
float d = tex2Dlod(_Splat, float4(WUV, 0, 0)).r * _Displacement;
v.vertex.xyz += v.normal * d;
}
float4 tessDistance (appdata v0, appdata v1, appdata v2)
{
float minDist = 10.0;
float maxDist = 50.0;
return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
float2 WUV = (IN.worldPos.xz - _SplatRemap.xy)/ _SplatRemap.zw;
half mask = tex2Dlod(_Splat, float4(WUV, 0, 0)).r;
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
fixed4 c2 = tex2D(_MainTex, IN.uv_MainTex) * _Color2;
float4 mix = lerp(c, c2, mask);
//o.Alpha = saturate(mix);
o.Albedo = mix.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Est-ce que quelqu'un peut me dire si c'est réalisable d'avoir un trait plus ou régulier et continu au lieu de tache, et quand le curseur reste fixe, la marque reste fixe.
Et si c'est possible, un petit tuyau pour y arriver.
Merci
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.
Re: Crayonner pour révéler un dessin
Il faut que tu conditionne l'appel à la fonction DoPaint() à une action sur le bouton de ta souris pour que ça ne dessine que lorsque tu le désires.
Et pour tracer des lignes, il faut mémoriser la position de la souris d'une frame à l'autre, et faire une boucle qui va répéter le tracé de la texture de brosse de dessin entre cette dernière position mémorisée et la position actuelle.
Et pour tracer des lignes, il faut mémoriser la position de la souris d'une frame à l'autre, et faire une boucle qui va répéter le tracé de la texture de brosse de dessin entre cette dernière position mémorisée et la position actuelle.
Re: Crayonner pour révéler un dessin
Oui, oui, c'est ce que j'avais fait. J'ai oublié de mettre les scriptsAlesk a écrit :Il faut que tu conditionne l'appel à la fonction DoPaint() à une action sur le bouton de ta souris pour que ça ne dessine que lorsque tu le désires.
C'est ce que je crois faire, voilà mes scriptsAlesk a écrit :Et pour tracer des lignes, il faut mémoriser la position de la souris d'une frame à l'autre, et faire une boucle qui va répéter le tracé de la texture de brosse de dessin entre cette dernière position mémorisée et la position actuelle.
Code : Tout sélectionner
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Crayonnage : MonoBehaviour
{
Camera Camera;
Transform World;
Transform Brush;
private void Start()
{
Camera = GameObject.Find("Camera").GetComponent<Camera>();
Brush = GameObject.Find("Brush").transform;
World = GameObject.Find("ExampleRevealRGB").transform;
}
private void Update()
{
if (Input.GetMouseButton(0))
{
//Extention permettant de voir si le curseur est sur le transform World
if (Camera.RayCastObjet(World))
{
//Extension permettant de repérer la position de la souris sur le transform
Brush.position = Camera.RaycastPos(World);
}
}
}
}
Code : Tout sélectionner
public static bool RayCastObjet(this Camera camera, Transform transform)
{
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
if (hit.transform == transform)
{
return true;
}
}
return false;
}
public static Vector3 RaycastPos(this Camera camera, Transform transform)
{
//on envoie le rayon vers la position de la souris
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
//s'il rencontre le transform
if (hit.transform == transform)
{
//retourne la position du clic;
return hit.point;
}
}
return Vector3.zero;
}
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.