are there any built-in classes that behave similarly to StreamGeometry class?
No, not really as Unity is a game engine. Most of the patterns you talk about come from the GDI brushes. However you can create your own texture, make it tilable and just set your UV coordinates accordingly and you should get the same effect.
Of course another option is to write a shader that produces the desired pattern. For example through a signed distance field (SDF) It’s just a matter of getting fancy with math ^^.
Just as an example, about a week ago I made a quick and dirty (about) to scale diagram of our sun-earth-moon system in order to see the actual umbra and preumbra of the earth shadow. The whole thing is just a single fullscreen “RawImage” with this shader:
Shader "Unlit/SolarSystemShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_SunPos("SunPos", Vector) = (0,0,0,1)
_SunSize("SunSize", float) = 140
_EarthPos("EarthPos", Vector) = (15000,0,0,1)
_EarthSize("EarthSize", float) = 1.2
_MoonPos("MoonPos", Vector) = (15038.44,0,0,1)
_MoonSize("MoonSize", float) = 0.3474
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _SunPos;
float _SunSize;
float4 _EarthPos;
float _EarthSize;
float4 _MoonPos;
float _MoonSize;
float3 _CamPos;
float _Aspect;
float _Scale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 drawCircle(float3 uv, float3 pos, float4 col, float size)
{
pos = uv - (pos - _CamPos)/_Scale;
size /= _Scale*2;
float d = sqrt(pos.x * pos.x + pos.y * pos.y);
d = smoothstep(1-size, 1-size + 0.003, 1-d);
return d * col;
}
fixed4 drawLine(float3 uv, float3 pos, float3 normal, float4 col, float size)
{
pos = uv-(pos - _CamPos) / _Scale;
float d = abs(dot(pos, normal));
d = smoothstep(1-size, 1-size + 0.001, 1-d);
return d * col*0.2;
}
fixed4 drawLineRepeat(float3 uv, float3 pos, float3 normal, float4 col, float size)
{
pos = uv + _CamPos /_Scale;
pos.x = frac(pos.x/100 * _Scale)*100/_Scale;
float d = abs(dot(pos, normal));
d = smoothstep(1 - size, 1 - size + 0.00001, 1 - d);
d *= smoothstep(0.001, 0.002, 0.1-abs(pos.y));
return d * col * 0.2;
}
fixed4 frag(v2f i) : SV_Target
{
float3 uv = float3(i.uv-0.5,0);
uv.x *= _Aspect;
fixed4 col = drawCircle(uv, _SunPos.xyz, float4(1, 1, 0.5, 1), _SunSize);
col += drawCircle(uv, _EarthPos.xyz, float4(0.5, 0.5, 1, 1), _EarthSize);
col += drawCircle(uv, _MoonPos.xyz, float4(0.5, 0.5, 0.5, 1), _MoonSize);
col += drawLine(uv, _SunPos.xyz, float3(1, 0, 0), float4(1, 0.5, 0.5, 0.1), 0.002);
col += drawLine(uv, _SunPos.xyz, float3(0, 1, 0), float4(1, 0.5, 0.5, 0.1), 0.002);
col += drawLine(uv, _EarthPos.xyz, float3(1, 0, 0), float4(1, 0.5, 0.5, 0.1), 0.002);
col += drawLine(uv, _MoonPos.xyz, float3(1, 0, 0), float4(1, 0.5, 0.5, 0.1), 0.002);
float3 p1 = float3(0, _SunSize*0.5 , 0);
float3 p2 = float3(_EarthPos.x, _EarthSize * 0.5, 0);
float3 n = normalize(cross(p1-p2, float3(0,0,1)));
col += drawLine(uv, p1, n, float4(0, 1, 0, 0.1), 0.002);
col += drawLine(uv, p1, float3(0, 1, 0), float4(0.5, 0.5, 1, 0.1), 0.002);
n.y = -n.y;
p1.y = -p1.y;
col += drawLine(uv, p1, n, float4(0, 1, 0, 0.1), 0.002);
col += drawLine(uv, p1, float3(0, 1, 0), float4(0.5, 0.5, 1, 0.1), 0.002);
n = normalize(cross(p1 - p2, float3(0, 0, 1)));
col += drawLine(uv, p1, n, float4(1, 1, 0, 0.1), 0.002);
n.y = -n.y;
p1.y = -p1.y;
col += drawLine(uv, p1, n, float4(1, 1, 0, 0.1), 0.002);
float3 p = 0;
col += drawLineRepeat(uv, p, float3(1,0,0), float4(1, 1, 0, 0.1), 0.002);
return col;
}
ENDCG
}
}
}
Besides that there is this simply script for the interaction. All it does is setting the “vitual” camera position and scale in the shader so you can pan around and zoom in and out. Though the whole diagram is just a single fullscreen UI.RawImage that does not move in screenspace.
Here’s the script that does the panning and zooming:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class SolarSystem : MonoBehaviour
{
public Material mat;
public Vector3 camPos;
public float z = 1f;
float aspect;
void Update()
{
if (mat == null)
return;
aspect = Screen.width / (float)Screen.height;
mat.SetVector("_CamPos", camPos);
mat.SetFloat("_Aspect", aspect);
mat.SetFloat("_Scale", z);
}
private void OnGUI()
{
var screenSizeInv = new Vector3(1f / Screen.height, -1f / Screen.height, 1);
Event e = Event.current;
var mPos = Vector3.Scale(e.mousePosition, screenSizeInv) - new Vector3(0.5f*aspect, -0.5f, 0);
GUILayout.BeginVertical("box");
float dist = (camPos.x + mPos.x * z)*10000;
GUILayout.Label("dist from sun: " + (dist.ToString("000,000,000km",System.Globalization.CultureInfo.InvariantCulture)));
GUILayout.Label("blue lines: horizontal to sun
green lines: tangents of sun and earth
yellow lines: crossing tangents");
GUILayout.EndVertical();
if (e.isMouse && Input.GetMouseButton(0))
{
var d = new Vector3(e.delta.x * screenSizeInv.x, e.delta.y * screenSizeInv.y);
camPos -= d*z;
}
else if (e.type == EventType.ScrollWheel)
{
var delta = (camPos + mPos * z);
if (e.delta.y <0)
z /= 1.1f;
else
z = 1.1f;
z = Mathf.Clamp(z, 0.00001f, 20000f);
camPos = (delta - mPosz);
}
camPos.y = Mathf.Clamp(camPos.y, -150, 150);
camPos.x = Mathf.Clamp(camPos.x, -150, 15100);
}
}
Note I just posted this as an example. In your case what you’re most likely interested in is the “drawLineRepeat” approach which draws the repeated yellow markers every 1 million kilometers. The “scale” of one unit (when we talk about the camera position and the position of the earth and moon) is given in one unit == 10000km. In your case it probably makes more sense to write a surface shader and use the screenspace position of a fragment, though it depends on your exact needs. The shader examples page has some great examples which may help in case you want to go down this route.
Though using an actual texture and let it tile across a quad may be enough for your usecase. Though we don’t know enough about your exact usecase so it’s quite difficult to recommend a particular method.